欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何避免Apache?Beanutils屬性copy

 更新時(shí)間:2023年01月13日 14:11:14   作者:哪吒編程  
這篇文章主要為大家介紹了如何避免Apache?Beanutils屬性copy的分析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

在實(shí)際的項(xiàng)目開(kāi)發(fā)中,對(duì)象間賦值普遍存在,隨著雙十一、秒殺等電商過(guò)程愈加復(fù)雜,數(shù)據(jù)量也在不斷攀升,效率問(wèn)題,浮出水面。

問(wèn):如果是你來(lái)寫對(duì)象間賦值的代碼,你會(huì)怎么做?

答:想都不用想,直接代碼走起來(lái),get、set即可。

問(wèn):下圖這樣?

答:對(duì)啊,你怎么能把我的代碼放到網(wǎng)上?

問(wèn):沒(méi),我只是舉個(gè)例子

答:這涉及到商業(yè)機(jī)密,是很嚴(yán)重的問(wèn)題

問(wèn):我發(fā)現(xiàn)你挺能扯皮啊,直接回答問(wèn)題行嗎?

答:OK,OK,我也覺(jué)得這樣寫很low,上次這么寫之后,差點(diǎn)挨打

  • 對(duì)象太多,ctrl c + strl v,鍵盤差點(diǎn)沒(méi)敲壞;
  • 而且很容易出錯(cuò),一不留神,屬性沒(méi)對(duì)應(yīng)上,賦錯(cuò)值了;
  • 代碼看起來(lái)很傻缺,一個(gè)類好幾千行,全是get、set復(fù)制,還起個(gè)了自以為很優(yōu)雅的名字transfer;
  • 如果屬性名不能見(jiàn)名知意,還得加上每個(gè)屬性的含義注釋(基本這種賦值操作,都是要加的,注釋很重要,注釋很重要,注釋很重要);
  • 代碼維護(hù)起來(lái)很麻煩;
  • 如果對(duì)象過(guò)多,會(huì)產(chǎn)生類爆炸問(wèn)題,如果屬性過(guò)多,會(huì)嚴(yán)重違背阿里巴巴代碼規(guī)約(一個(gè)方法的實(shí)際代碼最多20行);

問(wèn):行了,行了,說(shuō)說(shuō),怎么解決吧。

答:很簡(jiǎn)單啊,可以通過(guò)工具類Beanutils直接賦值啊

問(wèn):我聽(tīng)說(shuō)工具類最近很卷,你用的哪個(gè)???

答:就Apache自帶的那個(gè)啊,賊簡(jiǎn)單。我手寫一個(gè),給你欣賞一下。

問(wèn):你這代碼報(bào)錯(cuò)啊,避免用Apache Beanutils進(jìn)行屬性的copy。

答:沒(méi)報(bào)錯(cuò),只是嚴(yán)重警告而已,代碼能跑就行,有問(wèn)題再優(yōu)化唄

問(wèn):為啥會(huì)出現(xiàn)嚴(yán)重警告?

答:拿多少錢,干多少活,我又不是XXX,應(yīng)該是性能問(wèn)題吧

問(wèn):具體什么原因?qū)е碌哪兀?/p>

答:3000塊錢還得手撕一下 apache copyProperties 的源代碼唄?

通過(guò)單例模式調(diào)用copyProperties,但是,每一個(gè)方法對(duì)應(yīng)一個(gè)BeanUtilsBean.getInstance()實(shí)例,每一個(gè)類實(shí)例對(duì)應(yīng)一個(gè)實(shí)例,這不算一個(gè)真正的單例模式。

public static void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
	BeanUtilsBean.getInstance().copyProperties(dest, orig);
}

性能瓶頸 --> 日志太多也是病

通過(guò)源碼可以看到,每一個(gè)copyProperties都要進(jìn)行多次類型檢查,還要打印日志。

/**
 * org.apache.commons.beanutils.BeanUtils.copyProperties方法源碼解析
 * @author 哪吒編程
 * @time 2023-01-07
 */
public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException {
    // 類型檢查
    if (dest == null) {
        throw new IllegalArgumentException("No destination bean specified");
    } else if (orig == null) {
        throw new IllegalArgumentException("No origin bean specified");
    } else {
        // 打印日志
        if (this.log.isDebugEnabled()) {
            this.log.debug("BeanUtils.copyProperties(" + dest + ", " + orig + ")");
        }
        int var5;
        int var6;
        String name;
        Object value;
        // 類型檢查
        // DanyBean 提供了可以動(dòng)態(tài)修改實(shí)現(xiàn)他的類的屬性名稱、屬性值、屬性類型的功能
        if (orig instanceof DynaBean) {
            // 獲取源對(duì)象所有屬性
            DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
            DynaProperty[] var4 = origDescriptors;
            var5 = origDescriptors.length;
            for(var6 = 0; var6 < var5; ++var6) {
                DynaProperty origDescriptor = var4[var6];
                // 獲取源對(duì)象屬性名
                name = origDescriptor.getName();
                // 判斷源對(duì)象是否可讀、判斷目標(biāo)對(duì)象是否可寫
                if (this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
                    // 獲取對(duì)應(yīng)的值
                    value = ((DynaBean)orig).get(name);
                    // 每個(gè)屬性都調(diào)用一次copyProperty
                    this.copyProperty(dest, name, value);
                }
            }
        } else if (orig instanceof Map) {
            Map<String, Object> propMap = (Map)orig;
            Iterator var13 = propMap.entrySet().iterator();
            while(var13.hasNext()) {
                Map.Entry<String, Object> entry = (Map.Entry)var13.next();
                String name = (String)entry.getKey();
                if (this.getPropertyUtils().isWriteable(dest, name)) {
                    this.copyProperty(dest, name, entry.getValue());
                }
            }
        } else {
            PropertyDescriptor[] origDescriptors = this.getPropertyUtils().getPropertyDescriptors(orig);
            PropertyDescriptor[] var14 = origDescriptors;
            var5 = origDescriptors.length;
            for(var6 = 0; var6 < var5; ++var6) {
                PropertyDescriptor origDescriptor = var14[var6];
                name = origDescriptor.getName();
                if (!"class".equals(name) && this.getPropertyUtils().isReadable(orig, name) && this.getPropertyUtils().isWriteable(dest, name)) {
                    try {
                        value = this.getPropertyUtils().getSimpleProperty(orig, name);
                        this.copyProperty(dest, name, value);
                    } catch (NoSuchMethodException var10) {
                    }
                }
            }
        }
    }
}

通過(guò) jvisualvm.exe 檢測(cè)代碼性能

再通過(guò)jvisualvm.exe檢測(cè)一下運(yùn)行情況,果然,logging.log4j赫然在列,穩(wěn)居耗時(shí)Top1。

問(wèn):還有其它好的方式嗎?性能好一點(diǎn)的

答:當(dāng)然有,據(jù)我了解有 4 種工具類,實(shí)際上,可能會(huì)有更多,話不多說(shuō),先簡(jiǎn)單介紹一下。

org.apache.commons.beanutils.BeanUtils;

org.apache.commons.beanutils.PropertyUtils;

org.springframework.cglib.beans.BeanCopier;

org.springframework.beans.BeanUtils;

問(wèn):那你怎么不用?

答:OK,我來(lái)演示一下

package com.nezha.copy;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.util.StopWatch;
public class Test {
    public static void main(String[] args) {
        User user = new User();
        user.setUserId("1");
        user.setUserName("哪吒編程");
        user.setCardId("123");
        user.setCreateTime("2023-01-03");
        user.setEmail("666666666@qq.com");
        user.setOperate("哪吒");
        user.setOrgId("46987916");
        user.setPassword("123456");
        user.setPhone("10086");
        user.setRemark("456");
        user.setSex(1);
        user.setStatus("1");
        user.setTel("110");
        user.setType("0");
        user.setUpdateTime("2023-01-05");
        User target = new User();
        int sum = 10000000;
        apacheBeanUtilsCopyTest(user,target,sum);
        commonsPropertyCopyTest(user,target,sum);
        cglibBeanCopyTest(user,target,sum);
        springBeanCopyTest(user,target,sum);
    }
    private static void apacheBeanUtilsCopyTest(User source, User target, int sum) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < sum; i++) {
            apacheBeanUtilsCopy(source,target);
        }
        stopWatch.stop();
        System.out.println("使用org.apache.commons.beanutils.BeanUtils方式賦值"+sum+"個(gè)user對(duì)象,耗時(shí):"+stopWatch.getLastTaskTimeMillis()+"毫秒");
    }
    /**
     * org.apache.commons.beanutils.BeanUtils方式
     */
    private static void apacheBeanUtilsCopy(User source, User target) {
        try {
            BeanUtils.copyProperties(source, target);
        } catch (Exception e) {
        }
    }
    private static void commonsPropertyCopyTest(User source, User target, int sum) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < sum; i++) {
            commonsPropertyCopy(source,target);
        }
        stopWatch.stop();
        System.out.println("使用org.apache.commons.beanutils.PropertyUtils方式賦值"+sum+"個(gè)user對(duì)象,耗時(shí):"+stopWatch.getLastTaskTimeMillis()+"毫秒");
    }
    /**
     * org.apache.commons.beanutils.PropertyUtils方式
     */
    private static void commonsPropertyCopy(User source, User target) {
        try {
            PropertyUtils.copyProperties(target, source);
        } catch (Exception e) {
        }
    }
    private static void cglibBeanCopyTest(User source, User target, int sum) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < sum; i++) {
            cglibBeanCopy(source,target);
        }
        stopWatch.stop();
        System.out.println("使用org.springframework.cglib.beans.BeanCopier方式賦值"+sum+"個(gè)user對(duì)象,耗時(shí):"+stopWatch.getLastTaskTimeMillis()+"毫秒");
    }
    /**
     * org.springframework.cglib.beans.BeanCopier方式
     */
    static BeanCopier copier = BeanCopier.create(User.class, User.class, false);
    private static void cglibBeanCopy(User source, User target) {
        copier.copy(source, target, null);
    }
    private static void springBeanCopyTest(User source, User target, int sum) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        for (int i = 0; i < sum; i++) {
            springBeanCopy(source,target);
        }
        stopWatch.stop();
        System.out.println("使用org.springframework.beans.BeanUtils.copyProperties方式賦值"+sum+"個(gè)user對(duì)象,耗時(shí):"+stopWatch.getLastTaskTimeMillis()+"毫秒");
    }
    /**
     * org.springframework.beans.BeanUtils.copyProperties方式
     */
    private static void springBeanCopy(User source, User target) {
        org.springframework.beans.BeanUtils.copyProperties(source, target);
    }
}

"四大金剛" 性能統(tǒng)計(jì)

方法1000100001000001000000
apache BeanUtils906毫秒807毫秒1892毫秒11049毫秒
apache PropertyUtils17毫秒96毫秒648毫秒5896毫秒
spring cglib BeanCopier0毫秒1毫秒3毫秒10毫秒
spring copyProperties87毫秒90毫秒123毫秒482毫秒

不測(cè)不知道,一測(cè)嚇一跳,差的還真的多。

spring cglib BeanCopier性能最好,apache BeanUtils性能最差。

性能走勢(shì) --> spring cglib BeanCopier 優(yōu)于 spring copyProperties 優(yōu)于 apache PropertyUtils 優(yōu)于 apache BeanUtils

避免用Apache Beanutils進(jìn)行屬性的copy的問(wèn)題 上面分析完了,下面再看看其它的方法做了哪些優(yōu)化。

Apache PropertyUtils 源碼分析

從源碼可以清晰的看到,類型檢查變成了非空校驗(yàn),去掉了每一次copy的日志記錄,性能肯定更好了。

  • 類型檢查變成了非空校驗(yàn)
  • 去掉了每一次copy的日志記錄
  • 實(shí)際賦值的地方由copyProperty變成了DanyBean + setSimpleProperty;

DanyBean 提供了可以動(dòng)態(tài)修改實(shí)現(xiàn)他的類的屬性名稱、屬性值、屬性類型的功能。

/**
 * org.apache.commons.beanutils.PropertyUtils方式源碼解析
 * @author 哪吒編程
 * @time 2023-01-07
 */
public void copyProperties(Object dest, Object orig) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    // 判斷數(shù)據(jù)源和目標(biāo)對(duì)象不是null
    if (dest == null) {
        throw new IllegalArgumentException("No destination bean specified");
    } else if (orig == null) {
        throw new IllegalArgumentException("No origin bean specified");
    } else {
        // 刪除了org.apache.commons.beanutils.BeanUtils.copyProperties中最為耗時(shí)的log日志記錄
        int var5;
        int var6;
        String name;
        Object value;
        // 類型檢查
        if (orig instanceof DynaBean) {
            // 獲取源對(duì)象所有屬性
            DynaProperty[] origDescriptors = ((DynaBean)orig).getDynaClass().getDynaProperties();
            DynaProperty[] var4 = origDescriptors;
            var5 = origDescriptors.length;
            for(var6 = 0; var6 < var5; ++var6) {
                DynaProperty origDescriptor = var4[var6];
                // 獲取源對(duì)象屬性名
                name = origDescriptor.getName();
                // 判斷源對(duì)象是否可讀、判斷目標(biāo)對(duì)象是否可寫
                if (this.isReadable(orig, name) && this.isWriteable(dest, name)) {
                    try {
                        // 獲取對(duì)應(yīng)的值
                        value = ((DynaBean)orig).get(name);
                        // 相對(duì)于org.apache.commons.beanutils.BeanUtils.copyProperties此處有優(yōu)化
                        // DanyBean 提供了可以動(dòng)態(tài)修改實(shí)現(xiàn)他的類的屬性名稱、屬性值、屬性類型的功能
                        if (dest instanceof DynaBean) {
                            ((DynaBean)dest).set(name, value);
                        } else {
                            // 每個(gè)屬性都調(diào)用一次copyProperty
                            this.setSimpleProperty(dest, name, value);
                        }
                    } catch (NoSuchMethodException var12) {
                        if (this.log.isDebugEnabled()) {
                            this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var12);
                        }
                    }
                }
            }
        } else if (orig instanceof Map) {
            Iterator entries = ((Map)orig).entrySet().iterator();
            while(true) {
                Map.Entry entry;
                String name;
                do {
                    if (!entries.hasNext()) {
                        return;
                    }
                    entry = (Map.Entry)entries.next();
                    name = (String)entry.getKey();
                } while(!this.isWriteable(dest, name));
                try {
                    if (dest instanceof DynaBean) {
                        ((DynaBean)dest).set(name, entry.getValue());
                    } else {
                        this.setSimpleProperty(dest, name, entry.getValue());
                    }
                } catch (NoSuchMethodException var11) {
                    if (this.log.isDebugEnabled()) {
                        this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var11);
                    }
                }
            }
        } else {
            PropertyDescriptor[] origDescriptors = this.getPropertyDescriptors(orig);
            PropertyDescriptor[] var16 = origDescriptors;
            var5 = origDescriptors.length;
            for(var6 = 0; var6 < var5; ++var6) {
                PropertyDescriptor origDescriptor = var16[var6];
                name = origDescriptor.getName();
                if (this.isReadable(orig, name) && this.isWriteable(dest, name)) {
                    try {
                        value = this.getSimpleProperty(orig, name);
                        if (dest instanceof DynaBean) {
                            ((DynaBean)dest).set(name, value);
                        } else {
                            this.setSimpleProperty(dest, name, value);
                        }
                    } catch (NoSuchMethodException var10) {
                        if (this.log.isDebugEnabled()) {
                            this.log.debug("Error writing to '" + name + "' on class '" + dest.getClass() + "'", var10);
                        }
                    }
                }
            }
        }
    }
}

通過(guò) jvisualvm.exe 檢測(cè)代碼性能

再通過(guò)jvisualvm.exe檢測(cè)一下運(yùn)行情況,果然,logging.log4j沒(méi)有了,其他的基本不變。

Spring copyProperties 源碼分析

  • 判斷數(shù)據(jù)源和目標(biāo)對(duì)象的非空判斷改為了斷言;
  • 每次copy沒(méi)有日志記錄;
  • 沒(méi)有if (orig instanceof DynaBean) {這個(gè)類型檢查;
  • 增加了放開(kāi)權(quán)限的步驟;
/**
 * org.springframework.beans.BeanUtils.copyProperties方法源碼解析
 * @author 哪吒編程
 * @time 2023-01-07
 */
private static void copyProperties(Object source, Object target, @Nullable Class<?> editable,
                                   @Nullable String... ignoreProperties) throws BeansException {
    // 判斷數(shù)據(jù)源和目標(biāo)對(duì)象不是null
    Assert.notNull(source, "Source must not be null");
    Assert.notNull(target, "Target must not be null");
    /**
     * 若target設(shè)置了泛型,則默認(rèn)使用泛型
     * 若是 editable 是 null,則此處忽略
     * 一般情況下editable都默認(rèn)為null
     */
    Class<?> actualEditable = target.getClass();
    if (editable != null) {
        if (!editable.isInstance(target)) {
            throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
                    "] not assignable to Editable class [" + editable.getName() + "]");
        }
        actualEditable = editable;
    }
    // 獲取target中全部的屬性描述
    PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
    // 需要忽略的屬性
    List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
    for (PropertyDescriptor targetPd : targetPds) {
        Method writeMethod = targetPd.getWriteMethod();
        // 目標(biāo)對(duì)象存在寫入方法、屬性不被忽略
        if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
            PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
            if (sourcePd != null) {
                Method readMethod = sourcePd.getReadMethod();
                /**
                 * 源對(duì)象存在讀取方法、數(shù)據(jù)是可復(fù)制的
                 * writeMethod.getParameterTypes()[0]:獲取 writeMethod 的第一個(gè)入?yún)㈩愋?
                 * readMethod.getReturnType():獲取 readMethod 的返回值類型
                 * 判斷返回值類型和入?yún)㈩愋褪欠翊嬖诶^承關(guān)系,只有是繼承關(guān)系或相等的情況下,才會(huì)進(jìn)行注入
                 */
                if (readMethod != null &&
                        ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                    try {
                        // 放開(kāi)讀取方法的權(quán)限
                        if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                            readMethod.setAccessible(true);
                        }
                        // 通過(guò)反射獲取值
                        Object value = readMethod.invoke(source);
                        // 放開(kāi)寫入方法的權(quán)限
                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }
                        // 通過(guò)反射寫入值
                        writeMethod.invoke(target, value);
                    }
                    catch (Throwable ex) {
                        throw new FatalBeanException(
                                "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                    }
                }
            }
        }
    }
}

總結(jié)

阿里的友情提示,避免用Apache Beanutils進(jìn)行對(duì)象的copy,還是很有道理的。

Apache Beanutils 的性能問(wèn)題出現(xiàn)在類型校驗(yàn)和每一次copy的日志記錄;

Apache PropertyUtils 進(jìn)行了如下優(yōu)化:

  • 類型檢查變成了非空校驗(yàn)
  • 去掉了每一次copy的日志記錄
  • 實(shí)際賦值的地方由copyProperty變成了DanyBean + setSimpleProperty;

Spring copyProperties 進(jìn)行了如下優(yōu)化:

  • 判斷數(shù)據(jù)源和目標(biāo)對(duì)象的非空判斷改為了斷言;
  • 每次copy沒(méi)有日志記錄;
  • 沒(méi)有if (orig instanceof DynaBean) {這個(gè)類型檢查;
  • 增加了放開(kāi)權(quán)限的步驟;

以上就是如何避免Apache Beanutils屬性copy的詳細(xì)內(nèi)容,更多關(guān)于Apache Beanutils屬性copy的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java使用keySet方法獲取Map集合中的元素

    Java使用keySet方法獲取Map集合中的元素

    這篇文章主要為大家詳細(xì)介紹了Java使用keySet方法獲取Map集合中的元素,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-08-08
  • SpringBoot開(kāi)發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決

    SpringBoot開(kāi)發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決

    這篇文章主要介紹了SpringBoot開(kāi)發(fā)項(xiàng)目,引入JPA找不到findOne方法的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Maven提示jdk版本不正確的問(wèn)題

    Maven提示jdk版本不正確的問(wèn)題

    這篇文章主要介紹了Maven提示jdk版本不正確的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-04-04
  • 淺談Mybatis+mysql 存儲(chǔ)Date類型的坑

    淺談Mybatis+mysql 存儲(chǔ)Date類型的坑

    這篇文章主要介紹了淺談Mybatis+mysql 存儲(chǔ)Date類型的坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • java開(kāi)發(fā)使用BigDecimal避坑四則

    java開(kāi)發(fā)使用BigDecimal避坑四則

    這篇文章主要為大家介紹了java開(kāi)發(fā)使用BigDecimal的避坑四則,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • Java選擇結(jié)構(gòu)與循環(huán)結(jié)構(gòu)的使用詳解

    Java選擇結(jié)構(gòu)與循環(huán)結(jié)構(gòu)的使用詳解

    循環(huán)結(jié)構(gòu)是指在程序中需要反復(fù)執(zhí)行某個(gè)功能而設(shè)置的一種程序結(jié)構(gòu)。它由循環(huán)體中的條件,判斷繼續(xù)執(zhí)行某個(gè)功能還是退出循環(huán),選擇結(jié)構(gòu)用于判斷給定的條件,根據(jù)判斷的結(jié)果判斷某些條件,根據(jù)判斷的結(jié)果來(lái)控制程序的流程
    2022-03-03
  • Java非法字符: ‘\ufeff‘問(wèn)題及說(shuō)明

    Java非法字符: ‘\ufeff‘問(wèn)題及說(shuō)明

    這篇文章主要介紹了Java非法字符: ‘\ufeff‘問(wèn)題及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文(必看)

    IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文(必看)

    這篇文章主要介紹了IntelliJ Idea 2020.1 正式發(fā)布,官方支持中文了,本文通過(guò)截圖的形式給大家展示,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Java讀取文件的簡(jiǎn)單實(shí)現(xiàn)方法

    Java讀取文件的簡(jiǎn)單實(shí)現(xiàn)方法

    這篇文章主要介紹了Java讀取文件的簡(jiǎn)單實(shí)現(xiàn)方法,通過(guò)一個(gè)讀取txt格式的log文件為例,詳細(xì)的講述了Java讀取文件的方法及原理,需要的朋友可以參考下
    2014-09-09
  • Java中的StringTokenizer實(shí)現(xiàn)字符串切割詳解

    Java中的StringTokenizer實(shí)現(xiàn)字符串切割詳解

    這篇文章主要介紹了Java中的StringTokenizer實(shí)現(xiàn)字符串切割詳解,java.util工具包提供了字符串切割的工具類StringTokenizer,Spring等常見(jiàn)框架的字符串工具類(如Spring的StringUtils),需要的朋友可以參考下
    2024-01-01

最新評(píng)論