手寫程式碼實現spring的ioc功能
本篇文章和大家一起交流一下有關spring的ioc知識,並自己通過程式碼實現一個簡單版的spring的IOC功能。首先看一下IOC基礎知識的分享:
一、分享Iteye的開濤對Ioc的精彩講解
首先要分享的是Iteye的開濤這位技術牛人對Spring框架的IOC的理解,寫得非常通俗易懂,以下內容全部來自原文,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846
1.1、IoC是什麼
Ioc—Inversion of Control,即“控制反轉”,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味著將你設計好的物件交給容器控制,而不是傳統的在你的物件內部直接控制。
●誰控制誰,控制什麼:傳統Java SE程式設計,我們直接在物件內部通過new進行建立物件,是程式主動去建立依賴物件;而IoC是有專門一個容器來建立這些物件,即由Ioc容器來控制對 象的建立;誰控制誰?當然是IoC 容器控制了物件;控制什麼?那就是主要控制了外部資源獲取(不只是物件包括比如檔案等)。
●為何是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程式是由我們自己在物件中主動控制去直接獲取依賴物件,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴物件;為何是反轉?因為由容器幫我們查詢及注入依賴物件,物件只是被動的接受依賴物件,所以是反轉;哪些方面反轉了?依賴物件的獲取被反轉了。
用圖例說明一下,傳統程式設計如圖2-1,都是主動去建立相關物件然後再組合起來:
圖1-1 傳統應用程式示意圖
當有了IoC/DI的容器後,在客戶端類中不再主動去建立這些物件了,如圖2-2所示:
圖1-2有IoC/DI容器後程序結構示意圖
1.2、IoC能做什麼
IoC 不是一種技術,只是一種思想,一個重要的面向物件程式設計的法則,它能指導我們如何設計出鬆耦合、更優良的程式。傳統應用程式都是由我們在類內部主動建立依賴物件,從而導致類與類之間高耦合,難於測試;有了IoC容器後,把建立和查詢依賴物件的控制權交給了容器,由容器進行注入組合物件,所以物件與物件之間是 鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程式的整個體系結構變得非常靈活。
其實IoC對程式設計帶來的最大改變不是從程式碼上,而是從思想上,發生了“主從換位”的變化。應用程式原本是老大,要獲取什麼資源都是主動出擊,但是在IoC/DI思想中,應用程式就變成被動的了,被動的等待IoC容器來建立並注入它所需要的資源了。
IoC很好的體現了面向物件設計法則之一—— 好萊塢法則:“別找我們,我們找你”;即由IoC容器幫物件找相應的依賴物件並注入,而不是由物件主動去找。
1.3、IoC和DI
DI—Dependency Injection,即“依賴注入”:元件之間依賴關係由容器在執行期決定,形象的說,即由容器動態的將某個依賴關係注入到元件之中。依賴注入的目的並非為軟體系統帶來更多功能,而是為了提升元件重用的頻率,併為系統搭建一個靈活、可擴充套件的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何程式碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。
理解DI的關鍵是:“誰依賴誰,為什麼需要依賴,誰注入誰,注入了什麼”,那我們來深入分析一下:
●誰依賴於誰:當然是應用程式依賴於IoC容器;
●為什麼需要依賴:應用程式需要IoC容器來提供物件需要的外部資源;
●誰注入誰:很明顯是IoC容器注入應用程式某個物件,應用程式依賴的物件;
●注入了什麼:就是注入某個物件所需要的外部資源(包括物件、資源、常量資料)。
IoC和DI由什麼關係呢?其實它們是同一個概念的不同角度描述,由於控制反轉概念比較含糊(可能只是理解為容器控制物件這一個層面,很難讓人想到誰來維護物件關係),所以2004年大師級人物Martin Fowler又給出了一個新的名字:“依賴注入”,相對IoC 而言,“依賴注入”明確描述了“被注入物件依賴IoC容器配置依賴物件”。
看過很多對Spring的Ioc理解的文章,好多人對Ioc和DI的解釋都晦澀難懂,反正就是一種說不清,道不明的感覺,讀完之後依然是一頭霧水,感覺就是開濤這位技術牛人寫得特別通俗易懂,他清楚地解釋了IoC(控制反轉) 和DI(依賴注入)中的每一個字,讀完之後給人一種豁然開朗的感覺。我相信對於初學Spring框架的人對Ioc的理解應該是有很大幫助的。
二、分享Bromon的blog上對IoC與DI淺顯易懂的講解
2.1、IoC(控制反轉)
首先想說說IoC(Inversion of Control,控制反轉)。這是spring的核心,貫穿始終。所謂IoC,對於spring框架來說,就是由spring來負責控制物件的生命週期和物件間的關係。這是什麼意思呢,舉個簡單的例子,我們是如何找女朋友的?常見的情況是,我們到處去看哪裡有長得漂亮身材又好的mm,然後打聽她們的興趣愛好、qq號、電話號、ip號、iq號………,想辦法認識她們,投其所好送其所要,然後嘿嘿……這個過程是複雜深奧的,我們必須自己設計和麵對每個環節。傳統的程式開發也是如此,在一個物件中,如果要使用另外的物件,就必須得到它(自己new一個,或者從JNDI中查詢一個),使用完之後還要將物件銷燬(比如Connection等),物件始終會和其他的介面或類藕合起來。
那麼IoC是如何做的呢?有點像通過婚介找女朋友,在我和女朋友之間引入了一個第三者:婚姻介紹所。婚介管理了很多男男女女的資料,我可以向婚介提出一個列表,告訴它我想找個什麼樣的女朋友,比如長得像李嘉欣,身材像林熙雷,唱歌像周杰倫,速度像卡洛斯,技術像齊達內之類的,然後婚介就會按照我們的要求,提供一個mm,我們只需要去和她談戀愛、結婚就行了。簡單明瞭,如果婚介給我們的人選不符合要求,我們就會丟擲異常。整個過程不再由我自己控制,而是有婚介這樣一個類似容器的機構來控制。Spring所倡導的開發方式就是如此,所有的類都會在spring容器中登記,告訴spring你是個什麼東西,你需要什麼東西,然後spring會在系統執行到適當的時候,把你要的東西主動給你,同時也把你交給其他需要你的東西。所有的類的建立、銷燬都由 spring來控制,也就是說控制物件生存週期的不再是引用它的物件,而是spring。對於某個具體的物件而言,以前是它控制其他物件,現在是所有物件都被spring控制,所以這叫控制反轉。
2.2、DI(依賴注入)
IoC的一個重點是在系統執行中,動態的向某個物件提供它所需要的其他物件。這一點是通過DI(Dependency Injection,依賴注入)來實現的。比如物件A需要操作資料庫,以前我們總是要在A中自己編寫程式碼來獲得一個Connection物件,有了 spring我們就只需要告訴spring,A中需要一個Connection,至於這個Connection怎麼構造,何時構造,A不需要知道。在系統執行時,spring會在適當的時候製造一個Connection,然後像打針一樣,注射到A當中,這樣就完成了對各個物件之間關係的控制。A需要依賴 Connection才能正常執行,而這個Connection是由spring注入到A中的,依賴注入的名字就這麼來的。那麼DI是如何實現的呢? Java 1.3之後一個重要特徵是反射(reflection),它允許程式在執行的時候動態的生成物件、執行物件的方法、改變物件的屬性,spring就是通過反射來實現注入的。
理解了IoC和DI的概念後,一切都將變得簡單明瞭,剩下的工作只是在spring的框架中堆積木而已。
理論知識知道了,為了加深理解,我們自己可以手動寫程式碼實現一個簡單版的spring的IOC,看專案概況圖:annoation:定義常用的兩個依賴註解,一個是標緻service的,一個是service的屬性依賴,看具體程式碼
package com.ioc.spring.annoation; import java.lang.annotation.*; /** * @Author 18011618 * @Description 自定義服務的依賴注入 * @Date 16:12 2018/6/18 * @Modify By */ @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface IocService { String name() default ""; }
package com.ioc.spring.annoation; import java.lang.annotation.*; /** * @Author 18011618 * @Description 自定義屬性的依賴注入 * @Date 16:13 2018/6/18 * @Modify By */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface IocResource { }
service:定義兩個簡單演示的service,並且增加對應的註解
訂單服務:
package com.ioc.spring.service.impl; import com.ioc.spring.annoation.IocService; import com.ioc.spring.service.IOrderService; /** * @Author 18011618 * @Description * @Date 15:56 2018/6/18 * @Modify By */ @IocService public class OrderService implements IOrderService { public String findOrder(String username) { return "使用者"+username+"的訂單編號是:1001"; } }
使用者服務:依賴於訂單服務
package com.ioc.spring.service.impl; import com.ioc.spring.annoation.IocResource; import com.ioc.spring.annoation.IocService; import com.ioc.spring.service.IOrderService; import com.ioc.spring.service.IUserService; /** * @Author 18011618 * @Description * @Date 15:53 2018/6/18 * @Modify By */ @IocService(name = "userbiz") public class UserService implements IUserService { /*比較脆弱啊 這塊的屬性名稱一定要用實現類來命名 且 按照第一個字母要小寫的原則 否則很報錯的*/ @IocResource private IOrderService orderService; public String findOrder(String username) { return orderService.findOrder(username); } }
util:定義一個工具類主要掃描某個包路徑下面的所有class
package com.ioc.spring.util; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * 獲取某個包下面的所有類資訊 */ public class ClassUtil { /** * 取得某個介面下所有實現這個介面的類 */ public static List<Class> getAllClassByInterface(Class c) { List<Class> returnClassList = null; if (c.isInterface()) { // 獲取當前的包名 String packageName = c.getPackage().getName(); // 獲取當前包下以及子包下所以的類 List<Class<?>> allClass = getClasses(packageName); if (allClass != null) { returnClassList = new ArrayList<Class>(); for (Class classes : allClass) { // 判斷是否是同一個介面 if (c.isAssignableFrom(classes)) { // 本身不加入進去 if (!c.equals(classes)) { returnClassList.add(classes); } } } } } return returnClassList; } /* * 取得某一類所在包的所有類名 不含迭代 */ public static String[] getPackageAllClassName(String classLocation, String packageName) { // 將packageName分解 String[] packagePathSplit = packageName.split("[.]"); String realClassLocation = classLocation; int packageLength = packagePathSplit.length; for (int i = 0; i < packageLength; i++) { realClassLocation = realClassLocation + File.separator + packagePathSplit[i]; } File packeageDir = new File(realClassLocation); if (packeageDir.isDirectory()) { String[] allClassName = packeageDir.list(); return allClassName; } return null; } /** * 從包package中獲取所有的Class * @param packageName * @return */ public static List<Class<?>> getClasses(String packageName) { // 第一個class類的集合 List<Class<?>> classes = new ArrayList<Class<?>>(); // 是否迴圈迭代 boolean recursive = true; // 獲取包的名字 並進行替換 String packageDirName = packageName.replace('.', '/'); // 定義一個列舉的集合 並進行迴圈來處理這個目錄下的things Enumeration<URL> dirs; try { dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); // 迴圈迭代下去 while (dirs.hasMoreElements()) { // 獲取下一個元素 URL url = dirs.nextElement(); // 得到協議的名稱 String protocol = url.getProtocol(); // 如果是以檔案的形式儲存在伺服器上 if ("file".equals(protocol)) { // 獲取包的物理路徑 String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); // 以檔案的方式掃描整個包下的檔案 並新增到集合中 findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); } else if ("jar".equals(protocol)) { // 如果是jar包檔案 // 定義一個JarFile JarFile jar; try { // 獲取jar jar = ((JarURLConnection) url.openConnection()).getJarFile(); // 從此jar包 得到一個列舉類 Enumeration<JarEntry> entries = jar.entries(); // 同樣的進行迴圈迭代 while (entries.hasMoreElements()) { // 獲取jar裡的一個實體 可以是目錄 和一些jar包裡的其他檔案 如META-INF等檔案 JarEntry entry = entries.nextElement(); String name = entry.getName(); // 如果是以/開頭的 if (name.charAt(0) == '/') { // 獲取後面的字串 name = name.substring(1); } // 如果前半部分和定義的包名相同 if (name.startsWith(packageDirName)) { int idx = name.lastIndexOf('/'); // 如果以"/"結尾 是一個包 if (idx != -1) { // 獲取包名 把"/"替換成"." packageName = name.substring(0, idx).replace('/', '.'); } // 如果可以迭代下去 並且是一個包 if ((idx != -1) || recursive) { // 如果是一個.class檔案 而且不是目錄 if (name.endsWith(".class") && !entry.isDirectory()) { // 去掉後面的".class" 獲取真正的類名 String className = name.substring(packageName.length() + 1, name.length() - 6); try { // 新增到classes classes.add(Class.forName(packageName + '.' + className)); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } } } catch (IOException e) { e.printStackTrace(); } } } } catch (IOException e) { e.printStackTrace(); } return classes; } /** * 以檔案的形式來獲取包下的所有Class * * @param packageName * @param packagePath * @param recursive * @param classes */ public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, List<Class<?>> classes) { // 獲取此包的目錄 建立一個File File dir = new File(packagePath); // 如果不存在或者 也不是目錄就直接返回 if (!dir.exists() || !dir.isDirectory()) { return; } // 如果存在 就獲取包下的所有檔案 包括目錄 File[] dirfiles = dir.listFiles(new FileFilter() { // 自定義過濾規則 如果可以迴圈(包含子目錄) 或則是以.class結尾的檔案(編譯好的java類檔案) public boolean accept(File file) { return (recursive && file.isDirectory()) || (file.getName().endsWith(".class")); } }); // 迴圈所有檔案 for (File file : dirfiles) { // 如果是目錄 則繼續掃描 if (file.isDirectory()) { findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes); } else { // 如果是java類檔案 去掉後面的.class 只留下類名 String className = file.getName().substring(0, file.getName().length() - 6); try { // 新增到集合中去 classes.add(Class.forName(packageName + '.' + className)); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } }
context:定義模擬spring的上下文,初始化bean物件和對應的屬性物件
package com.ioc.spring.context; import com.ioc.spring.annoation.IocResource; import com.ioc.spring.annoation.IocService; import com.ioc.spring.util.ClassUtil; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; /** * @Author 18011618 * @Description 模擬spring容器 管理物件的依賴注入 * @Date 16:17 2018/6/18 * @Modify By */ public class SpringContext { private String path; /*String :beanId Object:serviceimpl*/ ConcurrentHashMap<String,Object>initBean = null; public SpringContext(String path){ this.path = path; } /** * 根據beanid獲取對應的bean * @param beanId * @return * @throws Exception */ public Object getBean(String beanId) throws Exception{ List<Class> classes = findAnnoationService(); if (classes == null || classes.isEmpty()) { throw new Exception("no found anything bean is useding initial.."); } initBean = initBean(classes); if (initBean == null || initBean.isEmpty()) { throw new Exception("initial bean is empty or null"); } Object object = initBean.get(beanId); //初始化屬性的依賴 initAttribute(object); return object; } /** * 初始化依賴的屬性* @param object * @throws IllegalArgumentException * @throws IllegalAccessException */ private void initAttribute(Object object)throws Exception{ //獲取object的所有型別 Class<? extends Object> classinfo = object.getClass(); //獲取所有的屬性欄位 Field[] fields = classinfo.getDeclaredFields(); //遍歷所有欄位 for(Field field : fields){ //查詢欄位上有依賴的註解boolean falg = field.isAnnotationPresent(IocResource.class); if (falg){ IocResource iocResource = field.getAnnotation(IocResource.class); if (iocResource!=null){ //獲取屬性的beanid String beanId = field.getName(); //獲取對應的object Object attrObject = getBean(beanId); if (attrObject!=null){ //訪問私有欄位 field.setAccessible(true); //賦值 field.set(object,attrObject); continue; } } } } } /** * 初始化bean * @param classes * @return * @throws IllegalAccessException * @throws InstantiationException */ public ConcurrentHashMap<String,Object>initBean(List<Class>classes) throws IllegalAccessException,InstantiationException{ ConcurrentHashMap<String,Object> map = new ConcurrentHashMap<String, Object>(); String beanId=""; for(Class clazz :classes){ Object object = clazz.newInstance(); IocService annotation =(IocService)clazz.getDeclaredAnnotation(IocService.class); if (annotation!=null){ //如果定義了name屬性 以實現的name屬性為主否則以預設的規則為主 String value = annotation.name(); if (value!=null && !value.equals("")){ beanId = value; } else { beanId = toLowerCaseFirstOne(clazz.getSimpleName()); } } //儲存值 map.put(beanId,object); } return map; } /** * 查詢包路徑下面所有添加註解的類 @IocService * @return * @throws Exception */ private List<Class>findAnnoationService()throws Exception{ if (path==null || path.equals("")){ throw new Exception("scan package address is null or empty.."); } //獲取包下面所有的類 List<Class<?>> classes = ClassUtil.getClasses(path); if (classes==null || classes.size()==0){ throw new Exception(相關推薦
手寫程式碼實現spring的ioc功能
本篇文章和大家一起交流一下有關spring的ioc知識,並自己通過程式碼實現一個簡單版的spring的IOC功能。首先看一下IOC基礎知識的分享:一、分享Iteye的開濤對Ioc的精彩講解 首先要分享的是Iteye的開濤這位技術牛人對Spring框架的IOC的理解,寫得非常
手寫一個實現基本功能的promse
`<!DOCTYPE html> <html> <head> <meta char
前端的幾種手寫程式碼實現
前言 現在的前端門檻越來越高,不再是隻會寫寫頁面那麼簡單。模組化、自動化、跨端開發等逐漸成為要求,但是這些都需要建立在我們牢固的基礎之上。不管框架和模式怎麼變,把基礎原理打牢才能快速適應市場的變化。下面介紹一些常用的原始碼實現: call實現 bind實現 new實現 instanceof實現 Object
知識點13:手寫程式碼-倒轉連結串列的c語言實現
寫在前面的廢話:筆記本壞掉了,一插入8g的記憶體卡就開不了機,而不插入的話可以開機,但是又啟動不了AS。不知道是記憶體卡的問題還是電腦介面的問題,想哭。這段時間要等同學帶他的電腦過來幫我測試,所以關於Android的內容暫時是寫不了的了~ 然後,秋招火爆到來,這段時間除了一邊繼續學習嵌
vue10行代碼實現上拉翻頁加載更多數據,純手寫js實現下拉刷新上拉翻頁不引用任何第三方插件
each ray 如果 部分 list 插件 下拉 ast 頁面數據 vue10行代碼實現上拉翻頁加載更多數據,純手寫js實現下拉刷新上拉翻頁不引用任何第三方插件/庫 一提到移動端的下拉刷新上拉翻頁,你可能就會想到iScroll插件,沒錯iScroll是一個高性能,資源占用
java 手寫程式碼簡單模擬SpringMVC
######1.在Spring MVC中,將一個普通的java類標註上Controller註解之後,再將類中的方法使用RequestMapping註解標註,那麼這個普通的java類就夠處理Web請求 ######2.通過一個簡單的java專案來模擬Spring MVC,先說一下整體思
程式設計師面試被HR要求手寫程式碼,網友:那是不是還得會修電腦?
面試時,被要求手寫程式碼,自信心爆棚的你,忽然有了提筆忘記的感覺,在一張紙上反覆塗塗畫畫,勉強寫出了一個功能,結果漏洞百出,面試過程相當不順利,丟下筆,對接下來的面試敷衍了事,結束後,繼而向周邊的朋友大吐苦水:都什麼年代了,還要求手寫程式碼?這公司真落後。 然而,這就是你與大神級別程式設計師,最
2 好籤電子簽章手寫籤批SDK功能特色
好籤電子籤批SDK,支援對PDF檔案,插入簽名、日期、文字、印章、圖片、流媒體等內容。並支援對簽名加密儲存,支援寫入CA證書,擁有多項自主智慧財產權筆跡驗籤技術。 1.1 簽名功能 功能特色:(截圖均為已上線功能) (1) 支援在PDF檔案上直接簽名 (2) 支
在程式設計競賽中,有6個評委為參賽選手打分,分數為0-100的整數分。 選手的最後得分為:去掉一個最高分和一個最低分的4個評委平均值 * 請寫程式碼實現(不考慮小數部分)
import java.util.Scanner; /* * 需求:在程式設計競賽中,有6個評委為參賽選手打分,分數為0-100的整數分。 * 選手的最後得分為:去掉一個最高分和一個最低分的4個評委平均值 * 請寫程式碼實現(不考慮小數部分) * *
程式設計師面試沒帶膝上型電腦,直接手寫程式碼 HR激動叫到:老鐵666
生活在這個忙碌的社會,我們總是在忙碌中丟三落四。我們會從家出門忘帶鑰匙,我們會上班路上想起忘帶工牌,到公司想起昨天晚上在家加班的資料沒拿,甚至上班時起身去打杯水,途中看了下手機回來忘記拿水杯。 最尷尬是也許就在於,去面試吃飯的傢伙沒帶了! 這是一個真實而感人的故事,程
寫程式碼實現棧溢位、堆溢位、永久代溢位、直接記憶體溢位
棧溢位(StackOverflowError) 堆溢位(OutOfMemoryError:Java heap space) 永久代溢位(OutOfMemoryError: PermGen space) 直接記憶體溢位 一、堆溢位 建立物件時如果沒有可以分配的堆記憶體,
web前端總結面試問題<經常遇到的手寫程式碼 - - >(二)
氣泡排序 var arr = [5,8,3,6,9] for(var i=0;i<arr.length;i++){ for(var j=i+1;j<arr.length;j++){ if(arr[i]>arr[j]){ v
技術面試常被用來作為手寫程式碼的考題彙總
考題1:二分查詢(遞迴與非遞迴) 遞迴方法 int BinSearch(int Array[],int low,int high,int key/*要找的值*/) { if
Appium初始化設定:手寫程式碼連線手機、appium-desktop連線手機
一、包名獲取的三種方式1)找開發要2)mac使用命令:adb logcat | grep START win使用命令:adb logcat | findstr START 檢視包名和入口如下: 3)通過aapt命令檢視 cmd到你的android-sdk-windows\bu
面試手寫程式碼的題目
提取出來的場景模擬:對於1–16,呼叫一次方法,就讓他產生4個數字,呼叫4次完畢,產生4組不同的數字! 注意:nextInt(101)是產生0-100之間的任意整數,不包括101. * 完整程式碼
Java面試手寫程式碼No.3(Singleton)
Java設計模式——單例模式(第3種為最終模式) 拋磚引玉 一:懶漢模式 /* * 問題域:設計一個能且只能產生一個物件的類 */ //單列模式一------懶漢模式 /* * 1、它是一種預載入的實現。不管程式碼中有沒有用到getInstance,都會被產生; *
手寫簡易版SpringIOC
SpringIOC就不在介紹了的吧,對容器建立,管理物件的過程非常有必要自己熟悉瞭解一下其中的生產規程。手寫一個簡易版的吧。(BeanFactory,ApplicationContext的,FileSystemXmlApplicationContext,ClassP
為什麼說手寫程式碼最能看出一個程式設計師的程式設計功底來?
記得初中第一次接觸程式設計的時候,那時學的是FoxBase,老師帶著大家用筆寫,沒有直接上機的,當時也沒覺得什麼,沒想到,現在回憶下當時手寫,鍛鍊語言是其次,真正鍛鍊的大腦對程式的思維邏輯,很管用。現在去各大公司應聘,往往第一輪筆試就會有手寫程式碼的考題,一畢業的時候面試一家
歸併排序 筆試面試手寫程式碼常考
歸併排序是將兩個或者兩個以上的有序序列進行合併的一種排序演算法。採用了分治的思想。 它的主要思路是將序列分為兩個子序列,對於兩個最終有序的子序列進行合併,得到有序的整體序列。 如何保證子序列有序呢?對子序列採用同樣的方式進行劃分,當子序列長度為1時,子序列有序,此時合
2018 安卓前端開發者工具,讓你擺脫手寫程式碼的煩惱
安卓前端快速開發工具-安卓切片安卓前端快速開發工具(安卓切片)是一款根據效果圖,切出佈局,設定好圖片,文字,文字框,圖片按鈕,列表框,資料來源,顯示資料欄位等屬性,就可以自動生成對應的layout xml檔案和對應的activity檔案(裡面自動生成了控制元件的定義、獲取、事