超萬能冷凍檸檬做法!家家都用得到,實在太實用了

檸檬含豐富的維生素C,經常食用,對增強身體抵抗力大有好處,但檸檬果實太酸,沒辦法直接吃

將檸檬切片用蜂蜜醃漬,裝瓶放入冰箱冷藏,即保持了檸檬的新鮮度,又可以隨吃隨拿,方便實用喔!「切片冷凍」比「隨切隨用」更能防止氧化或發酵,試試新的美容秘方吧~

萬用凍檸檬片的做法

主料:檸檬 (4粒)
調料:砂糖 (適量)
廚具:水果刀或其它

1.作法清水浸泡,以刷子流水刷凈農藥殘留

2.裝袋,冷凍庫冰凍2小時 (直接切片較厚)

3.取出凍檸檬,切去兩端

4.切片
冷凍2小時,外側果皮結構硬化,而內側果肉呈半凝固狀,易切薄不流汁

5.交叉排列入收納盒,冷凍保存

6.作法【+糖】接「作法4 」平盤內倒入砂糖,將檸檬片沾取砂糖,交叉排放進收納盒冷凍

雙面沾糖或單面沾糖皆可 

凍檸檬片應用廣泛:

搭配飲料加在可樂、茶飲、乳酸飲料
搓洗手指去蒜味
磨擦切菜板去味
浸泡水果防氧化
冰箱除臭

因檸檬的酸度較強,收納罐最好選用耐酸製品;喝完檸檬水記得用清水涮口,避免牙齒酸蝕喔~~~

本站聲明:網站內容來源http://www.look543.com,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

聽過「電子菸」嗎?想知道與一般傳統香菸有何不同嗎?

電子煙能幫助戒菸嗎?專家學者以健康觀點帶您來了解 !

新手該如何選擇電子菸口味及濃度呢?

香皂除了清潔外,竟然還能去異味增香!最後一項功能女生都需要

香皂是我們大家日常生活中,不可或缺的好幫手,但是你知道除了清潔之外,還有一些特別的小功用喔!

1、衛生間的鏡子遇到水霧會變的模糊不清,可以在鏡子面上塗一層香皂,然後用乾布擦乾凈,鏡子就不會變霧了,能持續一段時間呢

2、在白布上寫毛筆字時,在墨汁裡加一點香皂水,這樣布上的字跡會很清晰

3、在手錶的金屬殼上塗一點點香皂,再用布擦拭乾凈,可防止汗液侵蝕手錶

4、把香皂放入衣櫃、抽屜裡或者鞋櫃裡,都能起到芳香儲物空間的效果

另外在廁所裡也可以用到,比如將碎香皂放入瓶中,加入沸水使香皂變軟,這時就會散發出香味,還有清洗垃圾桶時用一些香皂水,也可以除臭味

5、新買來的繩子太硬時,可以把繩子泡在香皂水中浸泡幾分鐘,這樣繩子就會軟很多

6、竹、藤傢具比較髒時,可以用融化的香皂水來清洗

7、當您刷油漆前,最好在手上塗一層香皂,這樣哪怕沾上油漆,也很容易清洗

處理刷牆的刷子時,只要把刷子泡在肥皂水裡,十幾個小時後刷子很容易清洗乾淨

8、家裡的抽屜拉動很澀時,可以將香皂塗在軌道處,就會很滑了

同樣當百葉窗不好拉動時,只要在百葉窗的拉繩上塗一些香皂,再拉動幾次就很好用了

9、皮膚被蚊蟲叮咬後發癢,用香皂沾一點水,直接塗在咬叮的位置,很快就可止癢

再有如果皮膚上有小面積的水或火的輕度燙傷時,把燙傷的位置浸泡於香皂水中,能減輕一些灼痛感

10、香皂可以為鏡片去霧

戴口罩,一哈氣,眼鏡鏡片上一片霧氣,瞬間看不見了;從冰冷的室外走進屋裡,眼鏡鏡片上一片霧氣,瞬間看不見了

冬天的時候,各種火鍋、熱湯、熱茶、熱菜,眼鏡族們注意啦,香皂可以幫你對抗鏡片起霧的問題

11、香皂切碎後再裝進透氣性佳的袋子裡,即成了香熏袋,放進衣櫃或鞋櫃裡,有去異味防蟲增香作用

使用一陣子後,如果發現香味變淡,可以取出當清潔劑使用

12、香皂切碎後和蘇打粉按4:1的比例混合,用來清潔浴室的角落,如水龍頭等,去污效果更強

12、香皂的香味有驅蟑螂的作用

只要把香皂放進水裡搓洗後,用香皂水來噴蟑螂,即可堵住蟑螂身上的毛孔,蟑螂就窒息身亡了,比使用各種滅蟲劑或用鞋底打蟑螂安全簡單多了!

14、在新鞋子或磨腳的鞋子內部塗上薄薄的一層香皂,即可有效減少皮膚和鞋子之間的摩擦

本站聲明:網站內容來源http://www.look543.com,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

※聽過「電子菸」嗎?想知道與一般傳統香菸有何不同嗎?

電子煙能幫助戒菸嗎?專家學者以健康觀點帶您來了解 !

※新手該如何選擇電子菸口味及濃度呢?

冰箱塞太多雜亂又串味?聰明媽媽只用2個檸檬,立馬像新買來的一樣

冰箱裡之所以會產生異味,很大一部分原因其實是人們對於食材的隨意處理、放置,試想一下,一堆各式各樣的事物隨意的塞在冰箱裡,時間久了怎麼會沒異味呢?

因此,在教大家除異味的方法前,小編先跟大家講講如何做好冰箱裡的收納工作,以保持冰箱的整潔吧

1、食品裝袋/盒儲存,特別是水果和蔬菜等

水果、蔬菜等食材應先用水洗凈、晾乾、裝入清潔的保鮮袋中,再放入冷藏室內擱架上或果菜盒內進行儲藏

2、能冷凍儲藏的要冷凍儲藏

對需要在電冰箱中存放較長時間可以長期冷凍的食品,如肉、魚和蝦等食品,放入冷凍室儲藏,不要放入冷藏室內,以防變質

3、儲藏帶有內臟的食品

帶有內臟的食品,如雞、鴨和魚類等,必須先把內臟除去,以免內臟腐爛變質而污染其它食品,發生異味

4、生熟食品要分開儲藏

熟肉、香腸,火腿等熟食一定要用保鮮袋包紮好,放在專用的熟食擱架上,與生食及有強烈氣味的食品分開,以免和熟食污染

5、定期清洗冰箱

在使用過程中,要定期用中性洗潔劑和電冰箱除臭劑對箱內進行清洗,以防止箱內產生異味

二、如何去除冰箱裡的異味?

冰箱裡產生了異味要怎去除呢?很多人都知道可以利用橘子或柚子皮、檸檬等祛除異味,不過除了這些方法外,還有很多其他簡單實用的小妙招

1、竹炭去除法

大家都知道竹炭有吸附異味的作用,因此可以去超市買個小型的竹炭包,放到冰箱裡面,大約放1–2天後取出來在太陽底下暴晒,然後再放到冰箱裡繼續除味,一般放2–3次即可清除冰箱裡的異味

2、檸檬除異味

買幾個新鮮的檸檬,切成小塊後放到冰箱的角落裡,放置幾個小時後,再把檸檬切成更小的塊,不出幾天,冰箱裡的異味就會被清除了

3、茶葉除異味

大家可以把茶葉用紗布包起來,然後把茶葉包放入到冰箱裡面就行了,就算是我們日常生活中泡過的茶葉也可以,只要把它放在太陽下面曬乾就行了,不僅非常方便,還可以多次使用,效果也非常的好

4、木炭除異味

把木炭打成小塊的,然後用紗布包起來,這樣放在冰箱內就是非常好的選擇,這個跟竹炭包有異曲同工之妙,除臭效果是非常好的,大家都可以試試

5、食醋除異味

還有一種比較簡單的方法,就是把醋倒入敞開的玻璃瓶中,這樣把瓶子放在冰箱中就是非常好的

6、麵包除異味

大家還可以把吃剩下的麵包放在家裡的冰箱裡面,麵包的作用與除臭劑是完全相同的,所以它可以有效的去除冰箱中的異味

7、小蘇打除味

取500克小蘇打(碳酸氫鈉)分裝在兩個廣口玻璃瓶內(打開瓶蓋),放置在冰箱的上下層,異味能除

8、檀香皂除味

在冰箱內放1塊去掉包裝紙的檀香皂,除異味的效果亦佳。但冰箱內的熟食必須放在加蓋的容器中

學會了這些,再也不怕冰箱裡產生異味了,這麼實用的小妙招,不僅要告訴家人,更別忘了和朋友分享一下哦~

本站聲明:網站內容來源http://www.look543.com,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

※聽過「電子菸」嗎?想知道與一般傳統香菸有何不同嗎?

電子煙能幫助戒菸嗎?專家學者以健康觀點帶您來了解 !

※新手該如何選擇電子菸口味及濃度呢?

蓮藕別只炒著吃了!教你新做法,比吃肉還過癮

蓮藕一直是我最喜歡的食材之一,在眾多的烹飪當中,我最喜歡把它涼拌著吃,這個做法還是和我一位廚師親戚學的,也是他的飯店裡點菜率很高的一道菜。做法雖然看似簡單,但是想要好吃也是有訣竅的,掌握了裡面的技巧,就連剛學做飯的小夥伴也能做的成功率很高哦,話不多說,下面看看是怎麼做的吧。

1、首先我們選擇蓮藕的時候,一定買這種兩端帶頭的,這樣的蓮藕裡面沒有泥沙,清洗的時候沒有難度,把蓮藕的外皮削去,再清洗乾凈,切成薄片,切的越薄越好,這樣更容易入味。

2、因為蓮藕去皮之後,遇到空氣很容易氧化變黑,所以在切好之後,要趕緊的放進冷水中,再加一勺白醋,攪拌攪拌,浸泡10分鐘。

3、趁著浸泡蓮藕的時間,準備一些蒜末,蔥末,米椒圈,然後在鍋裡放比平時炒菜多一點的油,加一些花椒籽,用大火爆出香味,等到花椒籽炸到變色之後撈出不要,接著把油燒到很燙的狀態,把熱油澆在碗裡面,激發出食材的香味,然後往碗中加一勺鹽,一勺白糖,一勺蔬菜雞精,一勺白醋,一勺麻辣鮮,攪拌均勻備用。

4、蓮藕浸泡10分鐘之後,把它放進開水鍋中,大火煮2分鐘左右,煮至斷生後撈出,立即放進涼白開中冰一下,這樣可以使藕片的口感更加脆爽,更加的好吃。

5、把冰好的藕片撈出瀝乾水分,放在大一點的盆中,調好的料汁也倒進去,攪拌均勻,美味即成。

6、這樣做的涼拌藕片口感脆爽,百吃不膩,在大魚大肉的飯桌上,它才算是硬菜,怎麼吃都不會膩,比肉還要先光碟,不管是下飯還是下酒,都很受歡迎,大人小孩都適合吃,馬上就要過年了,學會後,可以在過年的時候露一手哦,喜歡的小夥伴趕緊試試做吧。

食材清單:

(蓮藕兩節,蔥薑蒜適量,油適量,花椒籽適量,白醋兩勺,鹽一勺,白糖一勺,麻辣鮮一勺,蔬菜雞精一勺)

烹飪小貼士:

這個家常菜可以一次多做一點,吃不完可以裝進保鮮盒中,放進冰箱密封冷藏,想吃的時候隨吃隨取,非常的方便,經過腌制之後,涼拌的藕片不但不會影響口感,而且比剛做出來的更好吃,更入味。

本站聲明:網站內容來源http://www.look543.com,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

※聽過「電子菸」嗎?想知道與一般傳統香菸有何不同嗎?

電子煙能幫助戒菸嗎?專家學者以健康觀點帶您來了解 !

※新手該如何選擇電子菸口味及濃度呢?

吃了30年玉米!這個做法還第一次見,加入牛奶後太香了

現在又到了吃玉米的季節, 很多人都奇怪,為什麼在外面吃的玉米,和自己在家煮的玉米口感不同?

其實外面賣的不是直接用水煮,而是鍋裡加了4樣其他的料,今天就分享一個煮玉米最好吃的做法,和外面賣的一樣,口感非常的好,下面看看是怎麼做的吧。

1、 首先我們要把玉米處理一下,把上面的玉米須擇乾凈,然後再用水把玉米清洗乾凈,掰成兩半放入鍋中,因為家裡的鍋不夠大,掰開之後可以節省空間,一次可以多煮一點。

2、 往鍋裡放一碗水,然後再加一盒純牛奶,20克的黃油,兩勺白糖,和一點點的鹽,這裡放鹽可以突出糖的甜度

3、 蓋上蓋子大火煮20分鐘,煮到10分鐘之後,我們把玉米翻一個面,使兩面都能夠吸收到湯汁,時間到了不需要燜,直接打開蓋子。

4、 用這個方法煮出來的玉米,香甜美味,奶香味十足,和買來的一樣好吃, 孩子特別的喜歡,如果你家孩子喜歡也試試做吧。

食材清單:

(玉米5根,水1碗,純牛奶1盒,黃油20克,白糖2勺,鹽一點點)

烹飪小貼士:

因為湯汁裡有牛奶和黃油,煮過的湯汁可以晾涼之後,裝在保鮮盒裡,放在冰箱裡保存,下次煮玉米的時候拿出來用,避免浪費(不過也不能放太久,大概是5天左右)

本站聲明:網站內容來源http://www.look543.com,如有侵權,請聯繫我們,我們將及時處理

【精選推薦文章】

※聽過「電子菸」嗎?想知道與一般傳統香菸有何不同嗎?

電子煙能幫助戒菸嗎?專家學者以健康觀點帶您來了解 !

※新手該如何選擇電子菸口味及濃度呢?

SpringBoot源碼學習系列之SpringMVC自動配置

目錄

源碼學習系列之WebMvc自動配置原理筆記

@

web的自動配置在SpringBoot項目中是一個很重要的方面,實現代碼在spring-boot-autoconfigure工程里:

按照官方文檔的說法,SpringBoot官方的說法,Springboot的SpringMVC自動配置,主要提供了如下自動配置:

WebMvcAutoConfiguration.java這個類很關鍵,這個就是SpringBoot Springmvc自動配置的一個很關鍵的配置類

@Configuration(proxyBeanMethods = false)//指定WebMvcAutoConfiguration不代理方法
@ConditionalOnWebApplication(type = Type.SERVLET)//在web環境(selvlet)才會起效
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })//系統有有Servlet,DispatcherServlet(Spring核心的分發器),WebMvcConfigurer的情況,這個自動配置類才起效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)//系統沒有WebMvcConfigurationSupport這個類的情況,自動配置起效
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
....
}

翻下源碼,可以看到WebMvcAutoConfiguration自動配置類里還有一個WebMvcConfigurer類型的配置類,2.2.1版本是implements WebMvcConfigurer接口,1.+版本是extends WebMvcConfigurerAdapter

@Configuration(proxyBeanMethods = false)//定義為配置類
    @Import(EnableWebMvcConfiguration.class)//spring底層註解,將EnableWebMvcConfiguration加到容器
    @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class })//使WebMvcProperties、ResourceProperties配置類生效
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    ....
}

1、ContentNegotiatingViewResolver

如圖,是視圖解析器的自動配置,這個類起效的情況是系統沒有ContentNegotiatingViewResolver類的情況,就調用改方法自動創建ContentNegotiatingViewResolver類

關鍵的是ContentNegotiatingViewResolver類,翻下ContentNegotiatingViewResolver類,找到如下重要的初始化方法

@Override
    protected void initServletContext(ServletContext servletContext) {
    //調用Spring的BeanFactoryUtils掃描容器里的所有視圖解析器ViewResolver類
        Collection<ViewResolver> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
        if (this.viewResolvers == null) {
            this.viewResolvers = new ArrayList<>(matchingBeans.size());
            //遍歷候選的viewResolvers,封裝到this.viewResolvers列表
            for (ViewResolver viewResolver : matchingBeans) {
                if (this != viewResolver) {
                    this.viewResolvers.add(viewResolver);
                }
            }
        }
        else {
            for (int i = 0; i < this.viewResolvers.size(); i++) {
                ViewResolver vr = this.viewResolvers.get(i);
                if (matchingBeans.contains(vr)) {
                    continue;
                }
                String name = vr.getClass().getName() + i;
                obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name);
            }

        }
        AnnotationAwareOrderComparator.sort(this.viewResolvers);
        this.cnmFactoryBean.setServletContext(servletContext);
    }

所以ContentNegotiatingViewResolver類的作用就是組合所有的視圖解析器,自動配置了ViewResolver(視圖解析器作用,根據方法返回值得到視圖對象view)

往下翻代碼,可以看到resolveViewName方法,裏面代碼是從this.viewResolvers獲取候選的視圖解析器,遍歷容器里所有視圖,然後通過如圖所標記的獲取候選視圖的方法,獲取候選的視圖列表,再通過getBestView獲取最合適的視圖

遍歷所有的視圖解析器對象,從視圖解析器里獲取候選的視圖,封裝成list保存

ok,跟了源碼就是只要將視圖解析器丟到Spring容器里,就可以加載到

寫個簡單的視圖解析類

DispatcherServlet是Spring核心分發器,找到doDispatch方法,debug,可以看到加的視圖解析器加載到了

2、靜態資源

也就是官方說的,如下圖所示:

翻譯過來就是支持靜態資源包括webjars的自動配置,webjars,就是以maven等等方式打成jar包的靜態資源,可以去看看文檔:

使用的話,直接去webjars官網負責對應的配置,加到項目里就可以

路徑都是在META-INF/webjars/**

WebMvcAutoConfiguration.addResourceHandlers,這個是比較重要的資源配置方法

@Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            if (!this.resourceProperties.isAddMappings()) {
                logger.debug("Default resource handling disabled");
                return;
            }
            Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
            //CacheControl是Spring框架提供的http緩存
            CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
            //讀取到webjars資源,將classpath:/META-INF/resources/webjars/的webjars資源都掃描出來
            if (!registry.hasMappingForPattern("/webjars/**")) {
                customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
                        .addResourceLocations("classpath:/META-INF/resources/webjars/")
                        .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
            }
            String staticPathPattern = this.mvcProperties.getStaticPathPattern();
            if (!registry.hasMappingForPattern(staticPathPattern)) {
                customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
                        .addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
                        .setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
            }
        }

ok,通過源碼可以知道,Springboot支持webjars和其它等等靜態資源,其它的靜態資源要放在如下目錄里,Springboot就能自動加載到

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/
  • classpath:/

3、自動註冊 Converter, GenericConverter, and Formatter beans.

翻譯過來就是自動註冊了 Converter, GenericConverter, and Formatter beans.

  • Converter:轉換器 ,作用就是能自動進行類型轉換
    eg: public String hello(User user),這是一個方法,然後前端視圖傳來的參數通過轉換器能夠根據屬性進行映射,然後進行屬性類型轉換
  • Formatter :格式化器,eg:比如對前端傳來的日期2019/11/25,進行格式化處理

源碼在這裏,WebMvcAutoConfiguration.addFormatters方法是添加格式化器的方法

同理,也是從Spring容器里將這幾種類拿過來

當然,還有其它的,比如WebMvcAutoConfiguration.localeResolver方法是實現i18n國際化語言支持的自動配置

@Bean
        @ConditionalOnMissingBean//沒有自定義localeResolver的情況
        @ConditionalOnProperty(prefix = "spring.mvc", name = "locale")//application.properties有配置了spring.mvc.locale
        public LocaleResolver localeResolver() {
            if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
                return new FixedLocaleResolver(this.mvcProperties.getLocale());
            }
            //默認使用AcceptHeaderLocaleResolver 
            AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
            localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
            return localeResolver;
        }

具體的源碼參考我之前博客:,博客裏面有涉及源碼的

4、支持HttpMessageConverters

HttpMessageConverters :消息轉換器,Springmvc中用來轉換http請求和響應的

源碼里是通過configureMessageConverters方法實現,很顯然也是從容器里獲取的

官方文檔里也進行了比較詳細描述,Springboot已經為我們自動配置了json的、xml的自動轉換器,當然你也可以自己添加

5、支持MessageCodesResolver

MessageCodesResolver:是消息解析器,WebMvcAutoConfiguration.getMessageCodesResolver是實現Exception異常信息格式的

WebMvcProperties配置文件定義的一個異常枚舉值

格式為如圖所示,定了了錯誤代碼是生成規則:

6、首頁支持

Springboot默認的首頁是index.html,也就是你在classpath路徑丟個index.html文件,就被Springboot默認為首頁,或者說歡迎頁

如圖示代碼,就是遍歷靜態資源文件,然後獲取index.html作為歡迎頁面

7、網站logo設置

Springboot1.+版本,是有默認的logo圖標的,2.2.1版本,經過全局搜索,沒有發現給自定義的圖標,使用的話,是直接丟在classpath路徑,文件命名為favicon.ico,不過在2.2.1代碼並沒有找到相應的配置代碼,1.+版本是有的,不過文檔還是有描述了

8、ConfigurableWebBindingInitializer 初始綁定器

跟下源碼,也是從Spring容器里獲取的,然後注意到,如果沒有這個ConfigurableWebBindingInitializer ,代碼就會調用基類的getConfigurableWebBindingInitializer

源碼,這裏也是創建一個getConfigurableWebBindingInitializer

ConfigurableWebBindingInitializer 是Springboot為系統自動配置的,當然我們也可以自己定義一個ConfigurableWebBindingInitializer ,然後加載到容器里即可

初始化綁定的方法,ok,本博客簡單跟一下源碼

注意:
ok,Springboot官方文檔里還有這樣的描述,如圖所示

意思是,在使用webmvcConfigurer配置的時候,不要使用@EnableWebMvc註解,為什麼不要使用呢?因為使用了@EnableWebMvc,就是實現全面接管SpringMVC自動配置,也就是說其它的自動配置都會失效,全部自己配置

原理是為什麼?可以簡單跟一下源碼,如圖,SpringMVC自動配置類,有這個很關鍵的註解,這個註解的意思是@WebMvcConfigurationSupport註解不在系統時候自動配置才起效

然後為什麼加了@EnableWebMvc自動配置就可以被全面接管?點一下@EnableWebMvc源碼

很顯然,DelegatingWebMvcConfiguration類extends WebMvcConfigurationSupport類,所以這也就是為什麼@EnableWebMvc註解能實現全面接管自動配置的原理

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

平板收購,iphone手機收購,二手筆電回收,二手iphone收購-全台皆可收購

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

js 關於apply和call的理解使用

  關於call和apply,以前也思考良久,很多時候都以為記住了,但是,我太難了。今天我特地寫下筆記,希望可以完全掌握這個東西,也希望可以幫助到任何想對學習這個東西的同學。

一.apply函數定義與理解,先從apply函數出發

  在MDN上,apply的定義是:

    “apply()方法調用一個具有給定this值的函,以及作為一個數組(或)提供的參數。”

  我的理解是:apply的前面有個含有this的對象,設為A,apply()的參數里,也含有一個含有this的對象設為B。則A.apply(B),表示A代碼執行調用了B,B代碼照常執行,執行后的結果作為apply的參數,然後apply把這個結果所指代表示的this替換掉A本身的this,接着執行A代碼。

  比如:

 1     var aa = {
 2         _name:111,
 3         _age:222,
 4         _f:function(){
 5             console.log(this)
 6             console.log(this._name)
 7         }
 8     }
 9     var cc = {
10         _name:0,
11         _age:0,
12         _f:function(){
13             console.log(this)
14             console.log(this._name)
15         }
16     }
17     cc._f.apply(aa)//此時aa表示的this就是aa本身
18     cc._f.apply(aa._f)//此時aa._f表示的this就是aa._f本身
19     
20     /**
21      * 此時aa._f()表示的this,就是執行后的結果本身。aa._f()執行后,沒有返回值,所以應該是undefined,而undefined作為call和apply的參數時,call和apply前面的方法 cc._f 的this會替換成全局對象window。
22      * 參考MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply 的參數說明
23      */
24     cc._f.apply(aa._f())

執行結果:

  1.參數為aa

  

  這兩行的打印的都是來自 cc._f 方法內的那兩句console 。aa的時候算是初始化,裏面的 aa._f 方法沒有執行。

  2.參數為aa.f

  

  這兩行的打印的都是來自 cc._f 方法內的那兩句console 。aa.f 的時候應該也算是初始化,或者是整個函數當參數傳但是不執行這個參數,即 aa._f 方法沒有執行。

  3.參數為aa.f()

   

  這四行的打印,前面兩行來自 aa._f() 方法執行打印的;後面兩行是來自cc._f()方法打印的。

  后兩行解析:aa._f()執行后,沒有返回值,所以是undefined,在apply執行解析后,cc._f()的this變成的window,所以打印了window。window裏面沒有_name這個屬性,所以undefined。

 二.apply與call的區分

  1.apply()

    A.apply(B, [1,2,3]) 後面的參數是arguments對象或類似數組的對象,它會被自動解析為A的參數;

    對於A.apply(B) / A.call(B) , 簡單講,B先執行,執行后根據結果去執行A。這個時候,用A去執行B的內容代碼,然後再執行自己的代碼。

  比如:

    var f1 = function(a,b){
        console.log(a+b)
    }
    var f2 = function(a,b,c){
        console.log(a,b,c)
    }
    f2.apply(f1,[1,2])//1 2 undefined

   先執行f1,f1執行后(f1是f1,不是f1()執行方法,所以console.log(a+b)這行代碼並沒有執行,相當於,初始化了代碼f1),由於沒有返回值,所以結果是undefined,f2執行的時候this指向window;參數中的 ” [1,2] “,解析后變成 f2 的參數 “ 1,2,undefined ”,執行f2方法后,打印出1,2,undefined三個值

  2.call()

    A.call(B, 1,2,3) 後面的參數都是獨立的參數對象,它們會被自動解析為A的參數;

  比如: 

    var f1 = function(a,b){
        console.log(a+b)
    }
    var f2 = function(a,b,c){
        console.log(a,b,c)
    }
    f2.call(f1,[1,2])//[1,2] undefined undefined
    f2.call(f1,1,2)//1 2 undefined

   參數中的 ” [1,2] “,因為傳入了一個數組,相當於只傳入了第一個參數,b和c參數沒有傳。解析后變成 f2 的參數 “ [1,2],undefined ,undefined ”,執行f2方法后,打印出 [1,2],undefined ,undefined 三個值。

三.apply與call帶來的便利

  1. push();

  push參數是類似(a,b,c,d,e)如此傳輸的,如果在一個數組的基礎上進行傳輸另一個數組的內容,可以如下:

    //apply用法
    var arr = new Array(1,2,3)
    var arr1 = new Array(11,21,31)
    Array.prototype.push.apply(arr,arr1)
    console.log(arr)//[1, 2, 3, 11, 21, 31]
    
    //call用法
    var arr = new Array(1,2,3)
    var arr1 = new Array(11,21,31)
    Array.prototype.push.call(arr,arr1[0],arr1[1],arr1[2])
    console.log(arr)//[1, 2, 3, 11, 21, 31]

   2. 數組利用Math求最大和最小值

  apply和call的第一個參數,如果是null或者undefined,則apply或call前面的函數會把this指向window

    //apply的用法
    var _maxNum = Math.max.apply(null,[1,3,2,4,5])
    console.log(_maxNum)//5
    var _minNum = Math.min.apply(null,[1,3,2,4,5])
    console.log(_minNum)//1
    
    //call的用法
    var _maxNum = Math.max.call(null,1,3,2,4,5)
    console.log(_maxNum)//5
    var _minNum = Math.min.call(null,1,3,2,4,5)
    console.log(_minNum)//1

 四.總結

  簡而言之,apply和call函數的第一個參數都是用來替換this指向的對象;apply的第二個參數使用arguments或者類似數組的參數進行傳參,call的第二個或以上的參數,使用獨立單位,一個一個進行傳參;執行順序是apply或call的第一個參數先執行得到結果,然後執行apply或call前面的函數,執行的時候用已經執行的結果所指代的this去執行。apply和call的使用除了參數上的使用方式不同外,功能是一模一樣的。

  以上內容純屬個人理解,有誤勿噴請指出!謝謝!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品在網路上成為最夯、最多人討論的話題?

※高價收購3C產品,價格不怕你比較

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

【集合系列】- 深入淺出的分析IdentityHashMap

一、摘要

在集合系列的第一章,咱們了解到,Map 的實現類有 HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties等等。

應該有很多人不知道 IdentityHashMap 的存在,其中不乏工作很多年的 Java 開發者,本文主要從數據結構和算法層面,探討 IdentityHashMap 的實現。

二、簡介

IdentityHashMap 的數據結構很簡單,底層實際就是一個 Object 數組,但是在存儲上並沒有使用鏈表來存儲,而是將 K 和 V 都存放在 Object 數組上。

當添加元素的時候,會根據 Key 計算得到散列位置,如果發現該位置上已經有改元素,直接進行新值替換;如果沒有,直接進行存放。當元素個數達到一定閾值時,Object 數組會自動進行擴容處理。

打開 IdentityHashMap 的源碼,可以看到 IdentityHashMap 繼承了AbstractMap 抽象類,實現了Map接口、可序列化接口、可克隆接口。

public class IdentityHashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, java.io.Serializable, Cloneable
{
    /**默認容量大小*/
    private static final int DEFAULT_CAPACITY = 32;
    
    /**最小容量*/
    private static final int MINIMUM_CAPACITY = 4;
    
    /**最大容量*/
    private static final int MAXIMUM_CAPACITY = 1 << 29;
    
    /**用於存儲實際元素的表*/
    transient Object[] table;
    
    /**數組大小*/
    int size;

    /**對Map進行結構性修改的次數*/
    transient int modCount;

    /**key為null所對應的值*/
    static final Object NULL_KEY = new Object();
    
    ......
}

可以看到類的底層,使用了一個 Object 數組來存放元素;在對象初始化時,IdentityHashMap 容量大小為64

public IdentityHashMap() {
        //調用初始化方法
        init(DEFAULT_CAPACITY);
}
private void init(int initCapacity) {
        //數組大小默認為初始化容量的2倍
        table = new Object[2 * initCapacity];
}

三、常用方法介紹

3.1、put方法

put 方法是將指定的 key, value 對添加到 map 里。該方法首先會對map做一次查找,通過==判斷是否存在key,如果有,則將舊value返回,將新value覆蓋舊value;如果沒有,直接插入,數組長度+1,返回null

源碼如下:

public V put(K key, V value) {
        //判斷key是否為空,如果為空,初始化一個Object為key
        final Object k = maskNull(key);

        retryAfterResize: for (;;) {
            final Object[] tab = table;
            final int len = tab.length;
            //通過key、length獲取數組小編
            int i = hash(k, len);
            
            //循環遍歷是否存在指定的key
            for (Object item; (item = tab[i]) != null;
                 i = nextKeyIndex(i, len)) {
                 //通過==判斷,是否數組中是否存在key
                if (item == k) {
                        V oldValue = (V) tab[i + 1];
                        //新value覆蓋舊value
                    tab[i + 1] = value;
                    //返回舊value
                    return oldValue;
                }
            }
            
            //數組長度 +1
            final int s = size + 1;
            //判斷是否需要擴容
            if (s + (s << 1) > len && resize(len))
                continue retryAfterResize;

            //更新修改次數
            modCount++;
            //將k加入數組
            tab[i] = k;
            //將value加入數組
            tab[i + 1] = value;
            size = s;
            return null;
        }
}

maskNull 函數,判斷 key 是否為空

private static Object maskNull(Object key) {
        return (key == null ? NULL_KEY : key);
}

hash 函數,通過 key 獲取 hash 值,結合數組長度通過位運算獲取數組散列下標

private static int hash(Object x, int length) {
        int h = System.identityHashCode(x);
        // Multiply by -127, and left-shift to use least bit as part of hash
        return ((h << 1) - (h << 8)) & (length - 1);
}

nextKeyIndex 函數,通過 hash 函數計算得到的數組散列下標,進行加2;因為一個 key、value 都存放在數組中,所以一個 map 對象佔用兩個數組下標,所以加2。

private static int nextKeyIndex(int i, int len) {
        return (i + 2 < len ? i + 2 : 0);
}

resize 函數,通過數組長度,進行擴容處理,擴容之後的長度為當前長度的2倍

private boolean resize(int newCapacity) {
        //擴容后的數組長度,為當前數組長度的2倍
        int newLength = newCapacity * 2;

        Object[] oldTable = table;
        int oldLength = oldTable.length;
        if (oldLength == 2 * MAXIMUM_CAPACITY) { // can't expand any further
            if (size == MAXIMUM_CAPACITY - 1)
                throw new IllegalStateException("Capacity exhausted.");
            return false;
        }
        if (oldLength >= newLength)
            return false;

        Object[] newTable = new Object[newLength];
        //將舊數組內容轉移到新數組
        for (int j = 0; j < oldLength; j += 2) {
            Object key = oldTable[j];
            if (key != null) {
                Object value = oldTable[j+1];
                oldTable[j] = null;
                oldTable[j+1] = null;
                int i = hash(key, newLength);
                while (newTable[i] != null)
                    i = nextKeyIndex(i, newLength);
                newTable[i] = key;
                newTable[i + 1] = value;
            }
        }
        table = newTable;
        return true;
}

3.2、get方法

get 方法根據指定的 key 值返回對應的 value。同樣的,該方法會循環遍曆數組,通過==判斷是否存在key,如果有,直接返回value,因為 key、value 是相鄰的存儲在數組中,所以直接在當前數組下標+1,即可獲取 value;如果沒有找到,直接返回null

值得注意的地方是,在循環遍歷中,是通過==判斷當前元素是否與key相同,如果相同,則返回value。咱們都知道,在 java 中,==對於對象類型參數,判斷的是引用地址,確切的說,是堆內存地址,所以,這裏判斷的是key的引用地址是否相同,如果相同,則返回對應的 value;如果不相同,則返回null

源碼如下:

public V get(Object key) {
        Object k = maskNull(key);
        Object[] tab = table;
        int len = tab.length;
        int i = hash(k, len);
        
        //循環遍曆數組,直到找到key或者,數組為空為值
        while (true) {
            Object item = tab[i];
            //通過==判斷,當前數組元素與key相同
            if (item == k)
                return (V) tab[i + 1];
            //數組為空
            if (item == null)
                return null;
            i = nextKeyIndex(i, len);
        }
}

3.3、remove方法

remove 的作用是通過 key 刪除對應的元素。該方法會循環遍曆數組,通過==判斷是否存在key,如果有,直接將keyvalue設置為null,對數組進行重新排列,返回舊 value。

源碼如下:

public V remove(Object key) {
        Object k = maskNull(key);
        Object[] tab = table;
        int len = tab.length;
        int i = hash(k, len);

        while (true) {
            Object item = tab[i];
            if (item == k) {
                modCount++;
                //數組長度減1
                size--;
                    V oldValue = (V) tab[i + 1];
                //將key、value設置為null
                tab[i + 1] = null;
                tab[i] = null;
                //刪除該元素后,需要把原來有衝突往後移的元素移到前面來
                closeDeletion(i);
                return oldValue;
            }
            if (item == null)
                return null;
            i = nextKeyIndex(i, len);
        }
}

closeDeletion 函數,刪除該元素后,需要把原來有衝突往後移的元素移到前面來,對數組進行重寫排列;

private void closeDeletion(int d) {
        // Adapted from Knuth Section 6.4 Algorithm R
        Object[] tab = table;
        int len = tab.length;

        Object item;
        for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
             i = nextKeyIndex(i, len) ) {
            int r = hash(item, len);
            if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {
                tab[d] = item;
                tab[d + 1] = tab[i + 1];
                tab[i] = null;
                tab[i + 1] = null;
                d = i;
            }
        }
}

四、總結

  1. IdentityHashMap 的實現不同於HashMap,雖然也是數組,不過IdentityHashMap中沒有用到鏈表,解決衝突的方式是計算下一個有效索引,並且將數據keyvalue緊挨着存在map中,即table[i]=keytable[i+1]=value

  2. IdentityHashMap 允許keyvalue都為null,當keynull的時候,默認會初始化一個Object對象作為key

  3. IdentityHashMap在保存、刪除、查詢數據的時候,以key為索引,通過==來判斷數組中元素是否與key相同,本質判斷的是對象的引用地址,如果引用地址相同,那麼在插入的時候,會將value值進行替換;

IdentityHashMap 測試例子:

public static void main(String[] args) {
        Map<String, String> identityMaps = new IdentityHashMap<String, String>();

        identityMaps.put(new String("aa"), "aa");
        identityMaps.put(new String("aa"), "bb");
        identityMaps.put(new String("aa"), "cc");
        identityMaps.put(new String("aa"), "cc");
        //輸出添加的元素
        System.out.println("數組長度:"+identityMaps.size() + ",輸出結果:" + identityMaps);
    }

輸出結果:

數組長度:4,輸出結果:{aa=aa, aa=cc, aa=bb, aa=cc}

儘管key的內容是一樣的,但是key的堆地址都不一樣,所以在插入的時候,插入了4條記錄。

五、參考

1、JDK1.7&JDK1.8 源碼

2、

3、

作者:炸雞可樂
出處:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※高價3c回收,收購空拍機,收購鏡頭,收購 MACBOOK-更多收購平台討論專區

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

收購3c瘋!各款手機、筆電、相機、平板,歡迎來詢價!

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

面試官:你連RESTful都不知道我怎麼敢要你?

目錄

面試官:了解RESTful嗎?

我:聽說過。

面試官:那什麼是RESTful?

我:就是用起來很規範,挺好的

面試官:是RESTful挺好的,還是自我感覺挺好的

我:都挺好的。

面試官:… 把門關上。

我:…. 要幹嘛?先關上再說。

面試官:我說出去把門關上。

我:what ?,奪門而去

@

01 前言

回歸正題,看過很多RESTful相關的文章總結,參齊不齊,結合工作中的使用,非常有必要歸納一下關於RESTful架構方式了,RESTful只是一種架構方式的約束,給出一種約定的標準,完全嚴格遵守RESTful標準並不是很多,也沒有必要。但是在實際運用中,有RESTful標準可以參考,是十分有必要的。

實際上在工作中對api接口規範、命名規則、返回值、授權驗證等進行一定的約束,一般的項目api只要易測試、足夠安全、風格一致可讀性強、沒有歧義調用方便我覺得已經足夠了,接口是給開發人員看的,也不是給普通用戶去調用。

02 RESTful的來源

REST:Representational State Transfer(表象層狀態轉變),如果沒聽說過REST,你一定以為是rest這個單詞,剛開始我也是這樣認為的,後來發現是這三個單詞的縮寫,即使知道了這三個單詞理解起來仍然非常晦澀難懂。如何理解RESTful架構,最好的辦法就是深刻理解消化Representational State Transfer這三個單詞到底意味着什麼。

1.每一個URI代表一種資源;

2.客戶端和服務器之間,傳遞這種資源的某種表現層;

3.客戶端通過四個HTTP動詞(get、post、put、delete),對服務器端資源進行操作,實現”表現層狀態轉化”。

是由美國計算機科學家Roy Fielding(百度百科沒有介紹,真是尷尬了)。Adobe首席科學家、Http協議的首要作者之一、Apache項目聯合創始人。

03 RESTful6大原則

REST之父Roy Fielding在論文中闡述REST架構的6大原則。

1. C-S架構

數據的存儲在Server端,Client端只需使用就行。兩端徹底分離的好處使client端代碼的可移植性變強,Server端的拓展性變強。兩端單獨開發,互不干擾。

2. 無狀態

http請求本身就是無狀態的,基於C-S架構,客戶端的每一次請求帶有充分的信息能夠讓服務端識別。請求所需的一些信息都包含在URL的查詢參數、header、body,服務端能夠根據請求的各種參數,無需保存客戶端的狀態,將響應正確返回給客戶端。無狀態的特徵大大提高的服務端的健壯性和可拓展性。

當然這總無狀態性的約束也是有缺點的,客戶端的每一次請求都必須帶上相同重複的信息確定自己的身份和狀態(這也是必須的),造成傳輸數據的冗餘性,但這種確定對於性能和使用來說,幾乎是忽略不計的。

3.統一的接口

這個才是REST架構的核心,統一的接口對於RESTful服務非常重要。客戶端只需要關注實現接口就可以,接口的可讀性加強,使用人員方便調用。

4.一致的數據格式

服務端返回的數據格式要麼是XML,要麼是Json(獲取數據),或者直接返回狀態碼,有興趣的可以看看博客園的開放平台的操作數據的api,post、put、patch都是返回的一個狀態碼 。

自我描述的信息,每項數據應該是可以自我描述的,方便代碼去處理和解析其中的內容。比如通過HTTP返回的數據裏面有 [MIME type ]信息,我們從MIME type裏面可以知道數據的具體格式,是圖片,視頻還是JSON,客戶端通過body內容、查詢串參數、請求頭和URI(資源名稱)來傳送狀態。服務端通過body內容,響應碼和響應頭傳送狀態給客戶端。這項技術被稱為超媒體(或超文本鏈接)。

除了上述內容外,HATEOS也意味着,必要的時候鏈接也可被包含在返回的body(或頭部)中,以提供URI來檢索對象本身或關聯對象。下文將對此進行更詳細的闡述。

如請求一條微博信息,服務端響應信息應該包含這條微博相關的其他URL,客戶端可以進一步利用這些URL發起請求獲取感興趣的信息,再如分頁可以從第一頁的返回數據中獲取下一頁的URT也是基於這個原理。

4.系統分層

客戶端通常無法表明自己是直接還是間接與端服務器進行連接,分層時同樣要考慮安全策略。

5.可緩存

在萬維網上,客戶端可以緩存頁面的響應內容。因此響應都應隱式或顯式的定義為可緩存的,若不可緩存則要避免客戶端在多次請求後用舊數據或臟數據來響應。管理得當的緩存會部分地或完全地除去客戶端和服務端之間的交互,進一步改善性能和延展性。

6.按需編碼、可定製代碼(可選)

服務端可選擇臨時給客戶端下發一些功能代碼讓客戶端來執行,從而定製和擴展客戶端的某些功能。比如服務端可以返回一些 Javascript 代碼讓客戶端執行,去實現某些特定的功能。
提示:REST架構中的設計準則中,只有按需編碼為可選項。如果某個服務違反了其他任意一項準則,嚴格意思上不能稱之為RESTful風格。

03 RESTful的7個最佳實踐

1. 版本

如github開放平台
https://developer.github.com/v3/

就是將版本放在url,簡潔明了,這個只有用了才知道,一般的項目加版本v1,v2,v3?好吧,這個加版本估計只有大公司大項目才會去使用,說出來不怕尷尬,我真沒用過。有的會將版本號放在header裏面,但是不如url直接了當。

https://example.com/api/v1/

2.參數命名規範

query parameter可以採用駝峰命名法,也可以採用下劃線命名的方式,推薦採用下劃線命名的方式,據說後者比前者的識別度要高,可能是用的人多了吧,因人而異,因團隊規範而異吧。

https://example.com/api/users/today_login 獲取今天登陸的用戶 
https://example.com/api/users/today_login&sort=login_desc 獲取今天登陸的用戶、登陸時間降序排列

3.url命名規範

API 命名應該採用約定俗成的方式,保持簡潔明了。在RESTful架構中,每個url代表一種資源所以url中不能有動詞,只能有名詞,並且名詞中也應該使用複數。實現者應使用相應的Http動詞GET、POST、PUT、PATCH、DELETE、HEAD來操作這些資源即可

不規範的的url,冗餘沒有意義,形式不固定,不同的開發者還需要了解文檔才能調用。

https://example.com/api/getallUsers GET 獲取所有用戶 
https://example.com/api/getuser/1 GET 獲取標識為1用戶信息 
https://example.com/api/user/delete/1 GET/POST 刪除標識為1用戶信息 
https://example.com/api/updateUser/1 POST 更新標識為1用戶信息 
https://example.com/api/User/add POST 添加新的用戶

規範后的RESTful風格的url,形式固定,可讀性強,根據users名詞和http動詞就可以操作這些資源

https://example.com/api/users GET 獲取所有用戶信息 
https://example.com/api/users/1 GET 獲取標識為1用戶信息 
https://example.com/api/users/1 DELETE 刪除標識為1用戶信息 
https://example.com/api/users/1 Patch 更新標識為1用戶部分信息,包含在body中 
https://example.com/api/users POST 添加新的用戶

4. 統一返回數據格式

對於合法的請求應該統一返回數據格式,這裏演示的是json

  • code——包含一個整數類型的HTTP響應狀態碼。
  • status——包含文本:”success”,”fail”或”error”。HTTP狀態響應碼在500-599之間為”fail”,在400-499之間為”error”,其它均為”success”(例如:響應狀態碼為1XX、2XX和3XX)。這個根據實際情況其實是可要可不要的。
  • message——當狀態值為”fail”和”error”時有效,用於显示錯誤信息。參照國際化(il8n)標準,它可以包含信息號或者編碼,可以只包含其中一個,或者同時包含並用分隔符隔開。
  • data——包含響應的body。當狀態值為”fail”或”error”時,data僅包含錯誤原因或異常名稱、或者null也是可以的

返回成功的響應json格式

{
  "code": 200,
  "message": "success",
  "data": {
    "userName": "123456",
    "age": 16,
    "address": "beijing"
  }
}

返回失敗的響應json格式

{
  "code": 401,
  "message": "error  message",
  "data": null
}

下面這個ApiResult的泛型類是在項目中用到的,拓展性強,使用方便。返回值使用統一的 ApiResult 或 ApiResult
錯誤返回 使用 ApiResult.Error 進行返回; 成功返回,要求使用 ApiResult.Ok 進行返回

public class ApiResult: ApiResult
    {
        public new static ApiResult<T> Error(string message)
        {
            return new ApiResult<T>
            {
                Code = 1,
                Message = message,
            };
        }
        [JsonProperty("data")]
        public T Data { get; set; }
    }
    public class ApiResult
    {
        public static ApiResult Error(string message)
        {
            return new ApiResult
            {
                Code = 1,
                Message = message,
            };
        }

        public static ApiResult<T> Ok<T>(T data)
        {
            return new ApiResult<T>()
            {
                Code = 0,
                Message = "",
                Data = data
            };
        }
        /// <summary>
        /// 0 是 正常 1 是有錯誤
        /// </summary>
        [JsonProperty("code")]
        public int Code { get; set; }
        [JsonProperty("msg")]
        public string Message { get; set; }

        [JsonIgnore]
        public bool IsSuccess => Code == 0;
    }

5. http狀態碼

在之前開發的xamarin android博客園客戶端的時候,patch、delete、post操作時body響應裏面沒有任何信息,僅僅只有http status code。HTTP狀態碼本身就有足夠的含義,根據http status code就可以知道刪除、添加、修改等是否成功。(ps:有點linux設計的味道哦,沒有返回消息就是最好的消息,表示已經成功了)服務段向用戶返回這些狀態碼並不是一個強制性的約束。簡單點說你可以指定這些狀態,但是不是強制的。常用HTTP狀態碼對照表
HTTP狀態碼也是有規律的

  • 1**請求未成功
  • 2**請求成功、表示成功處理了請求的狀態代碼。
  • 3**請求被重定向、表示要完成請求,需要進一步操作。 通常,這些狀態代碼用來重定向。
  • 4** 請求錯誤這些狀態代碼錶示請求可能出錯,妨礙了服務器的處理。
  • 5**(服務器錯誤)這些狀態代碼錶示服務器在嘗試處理請求時發生內部錯誤。 這些錯誤可能是服務器本身的錯誤,而不是請求出錯。

    6. 合理使用query parameter

    在請求數據時,客戶端經常會對數據進行過濾和分頁等要求,而這些參數推薦採用HTTP Query Parameter的方式實現

比如設計一個最近登陸的所有用戶
https://example.com/api/users?recently_login_day=3
搜索用戶,並按照註冊時間降序
https://example.com/api/users?recently_login_day=3
搜索用戶,並按照註冊時間升序、活躍度降序
https://example.com/api/users?q=key&sort=create_title_asc,liveness_desc
關於分頁,看看博客園開放平台分頁獲取精華區博文列表
https://api.cnblogs.com/api/blogposts/@picked?pageIndex={pageIndex}&pageSize={pageSize} 
返回示例: 
[ 
{ 
“Id”: 1, 
“Title”: “sample string 2”, 
“Url”: “sample string 3”, 
“Description”: “sample string 4”, 
“Author”: “sample string 5”, 
“BlogApp”: “sample string 6”, 
“Avatar”: “sample string 7”, 
“PostDate”: “2017-06-25T20:13:38.892135+08:00”, 
“ViewCount”: 9, 
“CommentCount”: 10, 
“DiggCount”: 11 
}, 
{ 
“Id”: 1, 
“Title”: “sample string 2”, 
“Url”: “sample string 3”, 
“Description”: “sample string 4”, 
“Author”: “sample string 5”, 
“BlogApp”: “sample string 6”, 
“Avatar”: “sample string 7”, 
“PostDate”: “2017-06-25T20:13:38.892135+08:00”, 
“ViewCount”: 9, 
“CommentCount”: 10, 
“DiggCount”: 11 
} 
]

7. 多表、多參數連接查詢如何設計URL

這是一個比較頭痛的問題,在做單個實體的查詢比較容易和規範操作,但是在實際的API並不是這麼簡單而已,這其中常常會設計到多表連接、多條件篩選、排序等。
比如我想查詢一個獲取在6月份的訂單中大於500元的且用戶地址是北京,用戶年齡在22歲到40歲、購買金額降序排列的訂單列表

https://example.com/api/orders?order_month=6&order_amount_greater=500&address_city=北京&sort=order_amount_desc&age_min=22&age_max=40

從這個URL上看,參數眾多、調用起來還得一個一個仔細對着,而且API本身非常不容易維護,命名看起來不是很容易,不能太長,也不能太隨意。

在.net WebAPI總我們可以使用屬性路由,屬性路由就是講路由附加到特定的控制器或操作方法上裝飾Controll及其使用[Route]屬性定義路由的方法稱為屬性路由。

這種好處就是可以精準地控制URL,而不是基於約定的路由,簡直就是為這種多表查詢量身定製似的的。 從webapi 2開發,現在是RESTful API開發中最推薦的路由類型。
我們可以在Controll中標記Route

[Route(“api/orders/{address}/{month}”)] 

Action中的查詢參數就只有金額、排序、年齡。減少了查詢參數、API的可讀性和可維護行增強了。

https://example.com/api/orders/beijing/6?order_amount_greater=500&sort=order_amount_desc&age_min=22&age_max=40

這種屬性路由比如在博客園開放的API也有這方面的應用,如獲取個人博客隨筆列表

請求方式:GET 
請求地址:https://api.cnblogs.com/api/blogs/{blogApp}/posts?pageIndex={pageIndex} 
(ps:blogApp:博客名)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

3c收購,鏡頭 收購有可能以全新價回收嗎?

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

賣IPHONE,iPhone回收,舊換新!教你怎麼賣才划算?

小白理解安卓虛擬機以及華為的’諾亞方舟’

虛擬機提到虛擬機,大家可能第一反應就是java中好像有虛擬機這個玩意。但是安卓中的虛擬機是什麼呢?是和java一樣的嗎?那麼我們先來了解一下java中的JVM!

JVM,搞java的肯定對它了解不少。JVM本質上就是一個軟件,是計算機硬件的一層軟件抽象,在這之上才幹夠運行Java程序,JAVA在編譯後會生成相似於彙編語言的JVM字節碼,與C語言編譯后產生的彙編語言不同的是,C編譯成的彙編語言會直接在硬件上跑。但JAVA編譯後生成的字節碼是在JVM上跑,須要由JVM把字節碼翻譯成機器指令。才幹使JAVA程序跑起來。JVM運行在操作系統上,屏蔽了底層實現的差異。從而有了JAVA吹噓的平台獨立性和Write Once Run Anywhere。依據JVM規範實現的詳細虛擬機有幾十種,主流的JVM包括Hotspot、Jikes RVM等。都是用C/C++和彙編編寫的,每一個JRE編譯的時候針對每一個平台編譯。因此下載JRE(JVM、Java核心類庫和支持文件)的時候是分平台的,JVM的作用是把平台無關的.class裏面的字節碼翻譯成平台相關的機器碼,來實現跨平台。

說白了,簡單點,就是:

                                                                      Java

                                                                      .java文件 -> .class文件 -> .jar文件

最後執行是class文件,有的會被再次打包成jar文件。

了解了這些之後,我們再去了解Android 中的虛擬機。

一、Dalvik虛擬機

Dalvik虛擬機( Dalvik Virtual Machine ),簡稱Dalvik VM或者DVM。這就是Android中的虛擬機。最初它的產生,是因為Google為了解決與Oracle之間關於Java相關專利和授權的糾紛,開發了DVM。

Android既然存在虛擬機,肯定也是在這個DVM上執行的。它的執行流程和JVM很像:

                                                                       Android

                                                                      .java文件 –> .class文件 -> .dex文件->.apk

DVM執行的是.dex格式文件,JVM執行的是.class文件,android程序編譯完之後生產.class文件,然後,dex工具會把.class文件處理成.dex文件,然後把資源文件和.dex文件等打包成.apk文件,apk就是android package的意思。

除了上面所說的,專利授權的原因除外,其實還有因為如下原因:

    dvm是基於寄存器的虛擬機,而jvm是基於虛擬棧的虛擬機。寄存器存取速度比棧快得多,dvm可以根據硬件實現最大的優化,比較適合移動設備。

    class文件存在很多的冗餘信息,dex工具會去除冗餘信息,並把所有的.class文件整合到.dex文件中,減少了I/O操作,提高了類的查找速度。

不光是上面這些差異,還有運行環境。  

   Dalvik : 一個應用啟動都運行一個單獨的虛擬機運行在一個單獨的進程中

   JVM: 只能運行一個實例, 也就是所有應用都運行在同一個JVM中

 

 這個是早先的安卓虛擬機,運行速度還是相當慢的。基於寄存器的虛擬機允許更快的執行時間,但代價是編譯后的程序更大。於是新的Dex字節碼格式odex產生了。它的作用等同於dex,只不過是dex優化后的格式。在App安裝的過程中,會通過Socket向/system/bin/install進程發送dex_opt的指令,對Dex文件進行優化。在DexClassLoader動態加載Dex文件時,也會進行Dex的優化,形成odex文件。

 

為了適應硬件速度的提升,隨後在Android 2.2的DVM中加入了JIT 編譯器(Just-In-Time Compiler)。Dalvik 使用 JIT 進行即時編譯,藉助 Java HotSpot VM,JIT 編譯器可以對執行次數頻繁的 dex/odex 代碼進行編譯與優化,將 dex/odex 中的 Dalvik Code(Smali 指令集)翻譯成相當精簡的 Native Code 去執行,JIT 的引入使得 Dalvik 的性能提升了 3~6 倍。

JIT編譯器的引入,提升了安裝速度,減少了佔用的空間,但隨之帶來的問題就是:多個dex加載會非常慢;JIT中的解釋器解釋的字節碼會帶來CPU和時間的消耗;還有熱點代碼的Monitor一直在運行帶來電量的損耗。

 

 這種情況下,手機動不動就卡是難以避免的。相信各位如果那時候用着Android手機,一定印象非常深刻。因為並不是那麼好用。

這樣的狀況一直持續到Andorid 4.4,帶來了全新的虛擬機運行環境 ART(Android RunTime)的預覽版和全新的編譯策略 AOT(Ahead-of-time)。但那時候。 ART 是和 Dalvik 共存的,用戶可以在兩者之間進行選擇(感覺很奇怪,作為一個愛好者,我當時看到這個東西可以切換都是不曉得是什麼玩意,用戶可都是小白啊,沒有必要共存的吧)。在Android 5.0的時候,ART 全面取代 Dalvik 成為 Android 虛擬機運行環境,至此。Dalvik 退出歷史舞台,AOT 也成為唯一的編譯模式。

二、ART 

AOT 和 JIT 的不同之處在於:JIT 是在運行時進行編譯,是動態編譯,並且每次運行程序的時候都需要對 odex 重新進行編譯;而 AOT 是靜態編譯,應用在安裝的時候會啟動 dex2oat 通過靜態編譯的方式,來將所有的dex文件(包括Multidex)編譯oat文件,編譯完后的oat其實是一個標準的ELF文件,只是相對於普通的ELF文件多加了oat data section以及oat exec section這兩個段而已。(這兩個段裏面主要保存了兩種信息:Dex的文件信息以及類信息和Dex文件編譯之後的機器碼)。預編譯成 ELF 文件,每次運行程序的時候不用重新編譯,是真正意義上的本地應用。運行的文件格式也從odex轉換成了oat格式。

 

其實在Android5.0的時候我們能夠明顯感覺手機好用很多就是因為這個原因,從根本上換掉了那種存在着無法解決弊端的虛擬機。在 Android 5.x 和 6.x 的機器上,系統每次 OTA 升級完成重啟的時候都會有個應用優化的過程,這個過程就是剛才所說的 dex2oat 過程,這個過程比較耗時並且會佔用額外的存儲空間。

AOT 模式的預編譯解決了應用啟動和運行速度和耗資源(電等)問題的同時也帶來了另外兩個問題:

      1、應用安裝和系統升級之後的應用優化比較耗時,並且會更耗時間。因為系統和apk都是越來越大的。

      2、優化后的文件會佔用額外的存儲空間

在經過了兩個Android大版本的穩定后,在Android7.0又再次迎來了JIT的 回歸。

JIT的回歸,可不是把AOT模式給取代了,而是形成 了AOT/JIT 混合編譯模式,這種模式至今仍在使用

應用在安裝的時候 dex 不會被編譯。

應用在運行時 dex 文件先通過解析器(Interpreter)後會被直接執行(這一步驟跟 Android 2.2 – Android 4.4之前的行為一致),與此同時,熱點函數(Hot Code)會被識別並被 JIT 編譯后存儲在 jit code cache 中並生成 profile 文件以記錄熱點函數的信息。

手機進入 IDLE(空閑) 或者 Charging(充電) 狀態的時候,系統會掃描 App 目錄下的 profile 文件並執行 AOT 過程進行編譯。

(Profile文件會在JIT運行的過程中生成:每個APP都會有自己的Profile文件,保存在App本身的Local Storage中。Profile會保存所調用的類以及函數的Index,通過profman工具進行分析生成)

 

 

個人理解:哪種模式擅長干什麼就讓他去干什麼。

混合編譯模式綜合了 AOT 和 JIT 的各種優點,使得應用在安裝速度加快的同時,運行速度、存儲空間和耗電量等指標都得到了優化。

之前一直在說流暢,真的流暢在Android7.0上才感受到了些許。Android7.0系統也被用了相當長的一段時間。之後的Android8.0和Android9.0都是對各方面的優化,例如編譯文件、編譯器、GC。。

其中,值得一提的是華為的方舟編譯器。

  • 首先會判斷該設備支不支持方舟編譯器,如果支持,則從應用商店下發方舟版本的包
  • 方舟編譯器會把dex文件通過自己的IR翻譯方舟格式的機器碼,據資料說也是一個ELF文件,但是會增加一些段,猜測是Dex中類信息相關的段
  • 通過這種方式,來消除Java與JNI之間的通信的損耗,以及提升運行時的效率
  • 在方舟內部,還重新完善了GC算法,使得GC的頻率大大降低,減少應用卡頓的現象
  • 目前方舟只支持64位的So,並且對於加殼的So會出現一些問題。

方舟編譯器適配的應用,下載手機上都是方舟版本的包,特製的包用方舟編譯器編譯效率大大提升,之後直接執行就可以了,直接略過了在ART虛擬機上預編譯的過程。這樣的結果是很完美的,但是卻也沒辦法跳過一個弊端。那就是生態。還是不管是安卓還是iOS,這麼多年的時間沉澱中,他們的生態系統早就達到了一個非常完善的地步。安卓和iOS應用已經多達上千萬,而方舟適配應用的數量還非常有限。

谷歌宣布將停止對華為提供安卓系統更新之後,華為曝光了自主研發的鴻蒙操作系統。當時網友各種力挺。不過後來,華為董事長梁華在談及鴻蒙系統時稱,鴻蒙系統是為物聯網開發的,用於自動駕駛、遠程醫療等低時延場景。鴻蒙系統是不是兩手準備我們不得而知。但是,一個操作系統最重要的就是它的生態環境。縱觀華為現在的整個格局,目的非常明確,用方舟編譯器來擴大自己的用戶群體。當用戶的基數足夠龐大時,可以隨時隨地建立一個完善的生態系統。如果在未來某一天,Android全面限制華為的使用之後,在這危機關頭鴻蒙系統還是很有可能扛起國產手機的一面大旗。哪怕不是鴻蒙,我們也需要這樣一個生態不是嗎?

最初,突然去了解Android中的虛擬機,一個是想要明白到底Android中的虛擬機和JVM是不是一回事,還有就是想要明白華為發布方舟編譯器到底快到了哪裡。

上述相關資料均來自網絡,侵權必刪。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※公開收購3c價格,不怕被賤賣!

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享