zoukankan      html  css  js  c++  java
  • fastjson SerializerFeature详细使用教程(特别注意:重复引用和循环引用问题)

    Fastjson介绍 

    https://github.com/alibaba/fastjson

    Fastjson是一个Java语言编写的JSON处理器,由阿里巴巴公司开发。
    1、遵循http://json.org标准,为其官方网站收录的参考实现之一。
    2、功能qiang打,支持JDK的各种类型,包括基本的JavaBean、Collection、Map、Date、Enum、泛型。
    3、无依赖,不需要例外额外的jar,能够直接跑在JDK上。
    4、开源,使用Apache License 2.0协议开源。http://code.alibabatech.com/wiki/display/FastJSON/Home
    5、具有超高的性能,java世界里没有其他的json库能够和fastjson可相比了。

    1.添加依赖

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.60</version>
    </dependency>

    Fastjson的最主要的使用入口是com.alibaba.fastjson.JSON

    import com.alibaba.fastjson.JSON;
     
    public static final Object parse(String text); // 把JSON文本parse为JSONObject或者JSONArray
    public static final JSONObject parseObject(String text); // 把JSON文本parse成JSONObject
    public static final <T> T parseObject(String text, Class<T> clazz); // 把JSON文本parse为JavaBean
    public static final JSONArray parseArray(String text); // 把JSON文本parse成JSONArray
    public static final <T> List<T> parseArray(String text, Class<T> clazz); //把JSON文本parse成JavaBean集合
    public static final String toJSONString(Object object); // 将JavaBean序列化为JSON文本
    public static final String toJSONString(Object object, boolean prettyFormat); // 将JavaBean序列化为带格式的JSON文本
    public static final Object toJSON(Object javaObject); 将JavaBean转换为JSONObject或者JSONArray。

     使用github.com/eishay/jvm-serializers/提供的程序做测试,性能数据如下:仅供参考

    2. SerializerFeature属性

    名称  含义  备注
    QuoteFieldNames 输出key时是否使用双引号,默认为true   
    UseSingleQuotes 使用单引号而不是双引号,默认为false    
    WriteMapNullValue   是否输出值为null的字段,默认为false  
    WriteEnumUsingToString  Enum输出name()或者original,默认为false 
    UseISO8601DateFormat    Date使用ISO8601格式输出,默认为false  
    WriteNullListAsEmpty    List字段如果为null,输出为[],而非null  
    WriteNullStringAsEmpty  字符类型字段如果为null,输出为”“,而非null  
    WriteNullNumberAsZero   数值字段如果为null,输出为0,而非null 
    WriteNullBooleanAsFalse Boolean字段如果为null,输出为false,而非null    
    SkipTransientField  如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true    
    SortField   按字段名称排序后输出。默认为false 
    WriteTabAsSpecial   把	做转义输出,默认为false   不推荐
    PrettyFormat    结果是否格式化,默认为false    
    WriteClassName  序列化时写入类型信息,默认为false。反序列化是需用到    
    DisableCircularReferenceDetect  消除对同一对象循环引用的问题,默认为false 
    WriteSlashAsSpecial 对斜杠’/’进行转义  
    BrowserCompatible   将中文都会序列化为uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false    
    WriteDateUseDateFormat  全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = “yyyy-MM-dd”;JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);    
    DisableCheckSpecialChar 一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false 
    NotWriteRootClassName   含义  
    BeanToArray 将对象转为array输出    
    WriteNonStringKeyAsString   含义  
    NotWriteDefaultValue    含义  
    BrowserSecure   含义  
    IgnoreNonFieldGetter    含义  
    WriteEnumUsingName  含义

    3. 演示示例

    3.1 编写实体类User,Word来模拟各种数据类型
    User实体如下(缺省setter,getter方法):

    public class User {
    
        private int id;
        private String name;
        private String add;
        private String old;
    }

    Word实体如下(缺省setter,getter方法):

    public class Word {
    
        private String d;
        private String e;
        private String f;
        private String a;
        private int b;
        private boolean c;
        private Date date;
        private Map<String , Object> map;
        private List<User> list;
    }

    3.2 编写Main进行测试
    初始化数据如下:

    public class Main {
        private static Word word;
    
        private static void init() {
            word = new Word();
            word.setA("a");
            word.setB(2);
            word.setC(true);
            word.setD("d");
            word.setE("");
            word.setF(null);
            word.setDate(new Date());
    
            List<User> list = new ArrayList<User>();
            User user1 = new User();
            user1.setId(1);
            user1.setOld("11");
            user1.setName("用户1");
            user1.setAdd("北京");
            User user2 = new User();
            user2.setId(2);
            user2.setOld("22");
            user2.setName("用户2");
            user2.setAdd("上海");
            User user3 = new User();
            user3.setId(3);
            user3.setOld("33");
            user3.setName("用户3");
            user3.setAdd("广州");
    
            list.add(user3);
            list.add(user2);
            list.add(null);
            list.add(user1);
    
            word.setList(list);
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("mapa", "mapa");
            map.put("mapo", "mapo");
            map.put("mapz", "mapz");
            map.put("user1", user1);
            map.put("user3", user3);
            map.put("user4", null);
            map.put("list", list);
            word.setMap(map);
        }
    
        public static void main(String[] args) {
            init();
            useSingleQuotes();
            // writeMapNullValue();
            // useISO8601DateFormat();
            // writeNullListAsEmpty();
            // writeNullStringAsEmpty();
            // sortField();
            // prettyFormat();
            // writeDateUseDateFormat();
            // beanToArray();
            //showJsonBySelf();
        }
    }

    3.2.1 UseSingleQuotes:使用单引号而不是双引号,默认为false

            /**
         * 1: UseSingleQuotes:使用单引号而不是双引号,默认为false
         */
        private static void useSingleQuotes() {
            System.out.println(JSONObject.toJSONString(word));
            System.out.println("设置useSingleQuotes后:");
            System.out.println(JSONObject.toJSONString(word,
                    SerializerFeature.UseSingleQuotes));
        }

    3.2.2 WriteMapNullValue:是否输出值为null的字段,默认为false

        /**
         * 2:WriteMapNullValue:是否输出值为null的字段,默认为false
         * 
         */
        private static void writeMapNullValue() {
            System.out.println(JSONObject.toJSONString(word));
            System.out.println("设置WriteMapNullValue后:");
            System.out.println(JSONObject.toJSONString(word, SerializerFeature.WriteMapNullValue));
        }

    3.2.3 UseISO8601DateFormat:Date使用ISO8601格式输出,默认为false

        /**
         * 3:UseISO8601DateFormat:Date使用ISO8601格式输出,默认为false
         * 
         */
        private static void useISO8601DateFormat() {
            System.out.println(JSONObject.toJSONString(word));
            System.out.println("设置UseISO8601DateFormat后:");
            System.out.println(JSONObject.toJSONString(word, SerializerFeature.UseISO8601DateFormat));
        }

    3.2.4 WriteNullListAsEmpty:List字段如果为null,输出为[],而非null,需要配合WriteMapNullValue使用,现将null输出

        /**
         * 4:
         * WriteNullListAsEmpty:List字段如果为null,输出为[],而非null
         * 需要配合WriteMapNullValue使用,现将null输出
         */
        private static void writeNullListAsEmpty() {
            word.setList(null);
            System.out.println(JSONObject.toJSONString(word));
            System.out.println("设置WriteNullListAsEmpty后:");
            System.out.println(JSONObject.toJSONString(word, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty));
        }

    3.2.5 WriteNullStringAsEmpty:字符类型字段如果为null,输出为””,而非null,需要配合WriteMapNullValue使用,现将null输出

        /**
         *  5:
         *  WriteNullStringAsEmpty:字符类型字段如果为null,输出为"",而非null
         *  需要配合WriteMapNullValue使用,现将null输出
         */
        private static void writeNullStringAsEmpty() {
            word.setE(null);
            System.out.println(JSONObject.toJSONString(word));
            System.out.println("设置WriteMapNullValue后:");
            System.out.println(JSONObject.toJSONString(word, SerializerFeature.WriteMapNullValue));
            System.out.println("设置WriteMapNullValue、WriteNullStringAsEmpty后:");
            System.out.println(JSONObject.toJSONString(word, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty));
        }

    3.2.6 SortField:按字段名称排序后输出。默认为false

        /**
         * SortField:按字段名称排序后输出。默认为false
         * 这里使用的是fastjson:为了更好使用sort field martch优化算法提升parser的性能,fastjson序列化的时候,
         * 缺省把SerializerFeature.SortField特性打开了。
         * 反序列化的时候也缺省把SortFeidFastMatch的选项打开了。
         * 这样,如果你用fastjson序列化的文本,输出的结果是按照fieldName排序输出的,parser时也能利用这个顺序进行优化读取。
         * 这种情况下,parser能够获得非常好的性能。
         */
        private static void sortField() {
            System.out.println(JSON.toJSONString(word));
            System.out.println(JSON.toJSONString(word, SerializerFeature.SortField));
        }

    3.2.7 PrettyFormat

     /**
         * 7:
         * PrettyFormat
         */
        private static void prettyFormat() {
            word.setMap(null);
            word.setList(null);
            System.out.println(JSON.toJSONString(word));
            System.out.println(JSON.toJSONString(word, SerializerFeature.PrettyFormat));
        }

    3.2.8 WriteDateUseDateFormat:全局修改日期格式,默认为false。

        /**
         * 8:
         * WriteDateUseDateFormat:全局修改日期格式,默认为false。
         */
        private static void writeDateUseDateFormat() {
            word.setMap(null);
            word.setList(null);
            System.out.println(JSON.toJSONString(word));
            JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";
            System.out.println(JSON.toJSONString(word, SerializerFeature.WriteDateUseDateFormat));
        }

    3.2.9 将对象转为array输出

         /**
         * 8:
         * 将对象转为array输出
         */
        private static void beanToArray() {
            word.setMap(null);
            word.setList(null);
            System.out.println(JSON.toJSONString(word));
            System.out.println(JSON.toJSONString(word, SerializerFeature.BeanToArray));
        }

    3.2.9 自定义

         /**
         * 9:自定义
         * 格式化输出
         * 显示值为null的字段
         * 将为null的字段值显示为""
         * DisableCircularReferenceDetect:消除循环引用
         */
        private static void showJsonBySelf() {
            System.out.println(JSON.toJSONString(word));
            System.out.println(JSON.toJSONString(word, SerializerFeature.PrettyFormat,
                    SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty,
                    SerializerFeature.DisableCircularReferenceDetect,
                    SerializerFeature.WriteNullListAsEmpty));
        }

    3.2.10 特别注意:循环引用检测 

    fastjson把对象转化成json避免$ref

    DisableCircularReferenceDetect来禁止循环引用检测:

    JSON.toJSONString(..., SerializerFeature.DisableCircularReferenceDetect)

    当进行toJSONString的时候,默认如果重用对象的话,会使用引用的方式进行引用对象。

    引用是通过"$ref"来表示

    引用 描述
    "$ref":".." 上一级
    "$ref":"@" 当前对象,也就是自引用
    "$ref":"$" 根对象
    "$ref":"$.children.0"

    基于路径的引用,相当于 root.getChildren().get(0)

    {"$ref":"../.."} 引用父对象的父对象

    重复引用

    指一个对象重复出现多次

    循环引用

    指你心里有我,我心里有你(互相引用),这个问题比较严重,如果处理不好就会出现StackOverflowError异常

    举例说明

    重复引用

    List<Object> list = new ArrayList<>();  
    Object obj = new Object();  
    list.add(obj);  
    list.add(obj);  
    

    循环引用

    // 循环引用的特殊情况,自引用  
    Map<String,Object> map = new HashMap<>();  
    map.put("map",map);  
    //  
    // map1引用了map2,而map2又引用map1,导致循环引用  
    Map<String,Object> map1 = new HashMap<>();  
    Map<String,Object> map2 = new HashMap<>();  
    map1.put("map",map2);  
    map2.put("map",map1);  

    简单说,重复引用就是一个集合/对象中的多个元素/属性同时引用同一对象,循环引用就是集合/对象中的多个元素/属性存在相互引用导致循环。 

    循环引用会触发的问题

    暂时不说重复引用,单说循环引用。
    一般来说,存在循环引用问题的集合/对象在序列化时(比如Json化),如果不加以处理,会触发StackOverflowError异常。

    分析原因:当序列化引擎解析map1时,它发现这个对象持有一个map2的引用,转而去解析map2。解析map2时,发现他又持有map1的引用,又转回map1。如此产生StackOverflowError异常。

    FastJson对重复/循环引用的处理

    首先,fastjson作为一款序列化引擎,不可避免的会遇到循环引用的问题,为了避免StackOverflowError异常,fastjson会对引用进行检测。

    如果检测到存在重复/循环引用的情况,fastjson默认会以“引用标识”代替同一对象,而非继续循环解析导致StackOverflowError。

    以下文两例说明,查看json化后的输出

    1.重复引用 JSON.toJSONString(list)

    [  
        {},  //obj的实体  
        {  
            "$ref": "$[0]"   //对obj的重复引用的处理  
        }  
    ]  
    

    2.循环引用 JSON.toJSONString(map1)

    {  
    // map1的key:value对  
        "map": {  
             // map2的key:value对  
            "map": {  
                 // 指向map1,对循环引用的处理  
                "$ref": ".."  
            }  
        }  
    }  
    

    重复引用的解决方法

    1.单个关闭 JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);
    2.全局配置关闭 JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask();
    
    

    局部的

    JSON.toJSONString(object, SerializerFeature.DisableCircularReferenceDetect);
    

    全局的

    普通的spring项目的话,用xml配置

    <mvc:annotation-driven>  
            <mvc:message-converters register-defaults="true">  
                <bean  
                    class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">  
                    <property name="supportedMediaTypes">  
                        <array>  
                            <value>text/html;charset=UTF-8</value>  
                        </array>  
                    </property>  
                    <property name="features">  
                        <array>  
                            <value>WriteMapNullValue</value>  
                            <value>WriteNullStringAsEmpty</value>  
                            <!-- 全局关闭循环引用检查,最好是不要关闭,不然有可能会StackOverflowException -->
                            <value>DisableCircularReferenceDetect</value>
                        </array>  
                    </property>  
                </bean>  
            </mvc:message-converters>  
        </mvc:annotation-driven>  
    

    如果springboot的话

    public class FastJsonHttpMessageConverterEx extends FastJsonHttpMessageConverter{
        public FastJsonHttpMessageConverterEx(){
            //在这里配置fastjson特性(全局设置的)
            FastJsonConfig fastJsonConfig = new FastJsonConfig();
            //fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");    //自定义时间格式
            //fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue);  //正常转换null值
            //fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);     //关闭循环引用
            this.setFastJsonConfig(fastJsonConfig);
        }
    
        @Override
        protected boolean supports(Class<?> clazz) {
            return super.supports(clazz);
        }
    }
    
    @Configuration
    public class WebMvcConfigurer extends WebMvcConfigurerAdapter {
        .....
        @Bean
        public FastJsonHttpMessageConverterEx fastJsonHttpMessageConverterEx(){
            return new FastJsonHttpMessageConverterEx();
        }
    }

    配置这个DisableCircularReferenceDetect的作用是:决定了生成的“多个”JSON对象中,是否加载被引用的同一个对象的数据。

    开启和关闭FastJson的“循环引用检测”特性的对比

    FastJson提供了SerializerFeature.DisableCircularReferenceDetect这个序列化选项,用来关闭引用检测。关闭引用检测后,重复引用对象时就不会被$ref代替,但是在循环引用时也会导致StackOverflowError异常。 

    避免重复引用序列化时显示$ref

    在编码时,使用新对象为集合或对象赋值,而非使用同一对象
    不要在多处引用同一个对象,这可以说是一种java编码规范,需要时刻注意。
    不要关闭FastJson的引用检测来避免显示$ref
    引用检测是FastJson提供的一种避免运行时异常的优良机制,如果为了避免在重复引用时显示$ref而关闭它,会有很大可能导致循环引用时发生StackOverflowError异常。这也是FastJson默认开启引用检测的原因。

    循环引用的解决方法:

    1.如果你前端用不到这个属性在该属性的get方法上加上注解@JSONField(serialize=false),
      这样该属性就不会被序列化出来,这个也可以解决重复引用
    2.修改表结构,出现循环引用了就是一个很失败的结构了,不然准备迎接StackOverflowError异常。

    对应输出结果如下:
    1、useSingleQuotes:


    2、writeMapNullValue:


    3、useISO8601DateFormat:


    4、writeNullListAsEmpty:


    5、writeNullStringAsEmpty:


    6、prettyFormat:


    7、writeDateUseDateFormat:


    8、beanToArray:


    9、自定义组合:showJsonBySelf:

    此时完整的输出如下:

    {"a":"a","b":2,"c":true,"d":"d","date":1473839656840,"e":"","list":[{"add":"广州","id":3,"name":"用户3","old":"33"},{"add":"上海","id":2,"name":"用户2","old":"22"},null,{"add":"北京","id":1,"name":"用户1","old":"11"}],"map":{"list":[{"$ref":"$.list[0]"},{"$ref":"$.list[1]"},null,{"$ref":"$.list[3]"}],"user3":{"$ref":"$.list[0]"},"mapz":"mapz","mapo":"mapo","mapa":"mapa","user1":{"$ref":"$.list[3]"}}}
    {
        "a":"a",
        "b":2,
        "c":true,
        "d":"d",
        "date":1473839656840,
        "e":"",
        "f":"",
        "list":[
            {
                "add":"广州",
                "id":3,
                "name":"用户3",
                "old":"33"
            },
            {
                "add":"上海",
                "id":2,
                "name":"用户2",
                "old":"22"
            },
            null,
            {
                "add":"北京",
                "id":1,
                "name":"用户1",
                "old":"11"
            }
        ],
        "map":{
            "list":[
                {
                    "add":"广州",
                    "id":3,
                    "name":"用户3",
                    "old":"33"
                },
                {
                    "add":"上海",
                    "id":2,
                    "name":"用户2",
                    "old":"22"
                },
                null,
                {
                    "add":"北京",
                    "id":1,
                    "name":"用户1",
                    "old":"11"
                }
            ],
            "user4":null,
            "user3":{
                "add":"广州",
                "id":3,
                "name":"用户3",
                "old":"33"
            },
            "mapz":"mapz",
            "mapo":"mapo",
            "mapa":"mapa",
            "user1":{
                "add":"北京",
                "id":1,
                "name":"用户1",
                "old":"11"
            }
        }
    }
     

    如果要被序列化的对象含有一个date属性或者多个date属性按照相同的格式序列化日期的话,那我们可以使用下面的语句实现:

    在应用的的Main方法体里配置全局参数:

    JSONObject.DEFFAULT_DATE_FORMAT="yyyy-MM-dd";//设置日期格式
    

    或者使用时传递配置参数

    JSONObject.toJSONString(resultMap, SerializerFeature.WriteMapNullValue,SerializerFeature.DisableCircularReferenceDetect,SerializerFeature.WriteDateUseDateFormat);
    

    但是上面的解决方案面临一个问题,如果不满足上面的条件(多个date属性,而且需要按照不定的格式序列化这些日期属性),那么我们就需要另辟蹊径,使用fastjson的特性来完成:

    @JSONField(format="yyyyMMdd")
    private Date date;
    
    @JSONField(format="yyyy-MM-dd HH:mm:ss")
    private Date date1;

    如果希望DTO转换输出的是下划线风格(fastjson默认驼峰风格),请使用:

    @JSONField(name="service_name")
    private String serviceName;

    想要全局配置的话,请在Main方法体中设置:

    //先执行static代码块,再执行该方法
    //是否输出值为null的字段,默认为false
    JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteMapNullValue.getMask();
    //数值字段如果为null,输出为0,而非null
    JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullNumberAsZero.getMask();
    //List字段如果为null,输出为[],而非null
    JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullListAsEmpty.getMask();
    //字符类型字段如果为null,输出为 "",而非null
    JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.WriteNullStringAsEmpty.getMask()

    PS:

    public enum SerializerFeature {
        QuoteFieldNames,//输出key时是否使用双引号,默认为true 
        /**
         * 
         */
        UseSingleQuotes,//使用单引号而不是双引号,默认为false
        /**
         * 
         */
        WriteMapNullValue,//是否输出值为null的字段,默认为false 
        /**
         * 
         */
        WriteEnumUsingToString,//Enum输出name()或者original,默认为false
        /**
         * 
         */
        UseISO8601DateFormat,//Date使用ISO8601格式输出,默认为false
        /**
         * @since 1.1
         */
        WriteNullListAsEmpty,//List字段如果为null,输出为[],而非null 
        /**
         * @since 1.1
         */
        WriteNullStringAsEmpty,//字符类型字段如果为null,输出为"",而非null 
        /**
         * @since 1.1
         */
        WriteNullNumberAsZero,//数值字段如果为null,输出为0,而非null 
        /**
         * @since 1.1
         */
        WriteNullBooleanAsFalse,//Boolean字段如果为null,输出为false,而非null
        /**
         * @since 1.1
         */
        SkipTransientField,//如果是true,类中的Get方法对应的Field是transient,序列化时将会被忽略。默认为true
        /**
         * @since 1.1
         */
        SortField,//按字段名称排序后输出。默认为false
        /**
         * @since 1.1.1
         */
        @Deprecated
        WriteTabAsSpecial,//把	做转义输出,默认为false
        /**
         * @since 1.1.2
         */
        PrettyFormat,//结果是否格式化,默认为false
        /**
         * @since 1.1.2
         */
        WriteClassName,//序列化时写入类型信息,默认为false。反序列化是需用到
    
        /**
         * @since 1.1.6
         */
        DisableCircularReferenceDetect,//消除对同一对象循环引用的问题,默认为false
    
        /**
         * @since 1.1.9
         */
        WriteSlashAsSpecial,//对斜杠'/'进行转义
    
        /**
         * @since 1.1.10
         */
        BrowserCompatible,//将中文都会序列化为uXXXX格式,字节数会多一些,但是能兼容IE 6,默认为false
    
        /**
         * @since 1.1.14
         */
        WriteDateUseDateFormat,//全局修改日期格式,默认为false。JSON.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd";JSON.toJSONString(obj, SerializerFeature.WriteDateUseDateFormat);
    
        /**
         * @since 1.1.15
         */
        NotWriteRootClassName,//暂不知,求告知
    
        /**
         * @since 1.1.19
         */
        DisableCheckSpecialChar,//一个对象的字符串属性中如果有特殊字符如双引号,将会在转成json时带有反斜杠转移符。如果不需要转义,可以使用这个属性。默认为false 
    
        /**
         * @since 1.1.35
         */
        BeanToArray //暂不知,求告知
        ;
    
        private SerializerFeature(){
            mask = (1 << ordinal());
        }
    
        private final int mask;
    
        public final int getMask() {
            return mask;
        }
    
        public static boolean isEnabled(int features, SerializerFeature feature) {
            return (features & feature.getMask()) != 0;
        }
    
        public static int config(int features, SerializerFeature feature, boolean state) {
            if (state) {
                features |= feature.getMask();
            } else {
                features &= ~feature.getMask();
            }
    
            return features;
        }
    }

    参照项目github地址:
    - https://github.com/gubaijin/buildmavenweb

  • 相关阅读:
    线程共享全局变量和私有全局变量
    线程退出前操作
    Linux下线程pid和tid
    几种常见的光纤接头(ST,SC,LC,FC)以及PC、APC和UPC的区别
    Javascript对象及数组用法笔记
    Javascript对象及数组用法笔记
    程序员特有的9个坏习惯
    程序员特有的9个坏习惯
    程序人生:真正的效率源自专注
    程序人生:真正的效率源自专注
  • 原文地址:https://www.cnblogs.com/candlia/p/11919878.html
Copyright © 2011-2022 走看看