最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Jackson反序列化@JsonFormat 不生效的解決方案

 更新時(shí)間:2021年08月10日 10:53:53   作者:無(wú)名后生  
這篇文章主要介紹了Jackson反序列化@JsonFormat 不生效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

今天在線上發(fā)現(xiàn)一個(gè)問(wèn)題,在使用Jackson進(jìn)行時(shí)間的反序列化時(shí),配置的 @JsonFormat 沒(méi)有生效

查看源碼發(fā)現(xiàn),Jackson在反序列化時(shí)間時(shí),會(huì)判斷json字段值類(lèi)型,如下:

由于在我們服務(wù)里,前端傳時(shí)間值到后端時(shí)采用了時(shí)間戳的方式,json值被判斷為數(shù)字類(lèi)型,所以Jackson在反序列化時(shí)直接簡(jiǎn)單粗暴的方式處理,將時(shí)間戳轉(zhuǎn)換為Date類(lèi)型:

為了能夠按照正確的格式解析時(shí)間,抹去后面的時(shí)間點(diǎn),精確到日,只好自定義一個(gè)時(shí)間解析器。自定義的時(shí)間解析器很好實(shí)現(xiàn),網(wǎng)上已經(jīng)有很多實(shí)例代碼,只需要繼承 JsonDeserializer<T> 就可以。

問(wèn)題的關(guān)鍵點(diǎn)在于,如何獲取到注解上的時(shí)間格式,按照注解上的格式去解析,否則每個(gè)解析器的實(shí)現(xiàn)只能使用一種固定的格式去解析時(shí)間。

1. 所以第一步是獲取注解上配置的信息

想要獲取字段對(duì)應(yīng)的注解信息,只有找到相應(yīng)的字段,然后通過(guò)字段屬性獲取注解信息,再通過(guò)注解信息獲取配置的格式。

但找了很久,也沒(méi)有在既有的參數(shù)里找到獲取相關(guān)字段的方法,只能去翻看源碼,最后在這里發(fā)現(xiàn)了獲取字段信息的方法以及解析器的生成過(guò)程,源代碼如下:

第一個(gè)紅框表示解析器是在這里生成的,第二個(gè)紅框就是獲取注解信息的地方

2. 注解獲取以后便創(chuàng)建自定義的時(shí)間解析器

猜想,我們可不可以也實(shí)現(xiàn)這個(gè)類(lèi),重寫(xiě)生成解析器的方法?那就試試唄~ 我們?cè)谧远x的時(shí)間解析器上同樣實(shí)現(xiàn)這個(gè)類(lèi),重寫(xiě)了生成時(shí)間解析器的方法,并初始化一些自定義的信息供解析時(shí)間使用(當(dāng)然猜想是正確的,因?yàn)楣俜骄褪沁@么搞的,只是官方的是一個(gè)內(nèi)部類(lèi)實(shí)現(xiàn)的),具體代碼如下:

時(shí)間解析器代碼:

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import com.google.common.collect.Lists;
import com.tujia.rba.framework.core.remote.api.BizErrorCode;
import com.tujia.rba.framework.core.remote.api.BizException;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @author 無(wú)名小生 Date: 2019-02-19 Time: 19:00
 * @version $Id$
 */
public class DateJsonDeserializer extends JsonDeserializer<Date> implements ContextualDeserializer {
    private final static Logger logger = LoggerFactory.getLogger(DateJsonDeserializer.class);
    private final static List<String> FORMATS = Lists.newArrayList(
        "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyyMMdd-HHmmss", "yyyy-MM-dd", "MM-dd", "HH:mm:ss", "yyyy-MM"
    );
    public final DateFormat df;
    public final String formatString;
    public DateJsonDeserializer() {
        this.df = null;
        this.formatString = null;
    }
    public DateJsonDeserializer(DateFormat df) {
        this.df = df;
        this.formatString = "";
    }
    public DateJsonDeserializer(DateFormat df, String formatString) {
        this.df = df;
        this.formatString = formatString;
    }
    @Override
    public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        try {
            String dateValue = p.getText();
            if (df == null || StringUtils.isEmpty(dateValue)) {
                return null;
            }
            logger.info("使用自定義解析器解析字段:{}:時(shí)間:{}",p.getCurrentName(),p.getText());
            Date date;
            if (StringUtils.isNumeric(dateValue)){
                date = new Date(Long.valueOf(dateValue));
            }else {
                String[] patterns = FORMATS.toArray(new String[0]);
                date = DateUtils.parseDate(p.getText(),patterns);
            }
            return df.parse(df.format(date));
        } catch (ParseException | SecurityException e) {
            logger.error("JSON反序列化,時(shí)間解析失敗", e);
            throw new BizException(BizErrorCode.UNEXPECTED_ERROR);
        }
    }
    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        if (property != null) {
            JsonFormat.Value format = ctxt.getAnnotationIntrospector().findFormat(property.getMember());
            if (format != null) {
                TimeZone tz = format.getTimeZone();
                // First: fully custom pattern?
                if (format.hasPattern()) {
                    final String pattern = format.getPattern();
                    if (!FORMATS.contains(pattern)){
                        FORMATS.add(pattern);
                    }
                    final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
                    SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
                    if (tz == null) {
                        tz = ctxt.getTimeZone();
                    }
                    df.setTimeZone(tz);
                    return new DateJsonDeserializer(df, pattern);
                }
                // But if not, can still override timezone
                if (tz != null) {
                    DateFormat df = ctxt.getConfig().getDateFormat();
                    // one shortcut: with our custom format, can simplify handling a bit
                    if (df.getClass() == StdDateFormat.class) {
                        final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
                        StdDateFormat std = (StdDateFormat) df;
                        std = std.withTimeZone(tz);
                        std = std.withLocale(loc);
                        df = std;
                    } else {
                        // otherwise need to clone, re-set timezone:
                        df = (DateFormat) df.clone();
                        df.setTimeZone(tz);
                    }
                    return new DateJsonDeserializer(df);
                }
            }
        }
        return this;
    }
}

至此,自定義時(shí)間解析器就完成了

但是,為了能夠更靈活的控制時(shí)間的解析(例如:輸入的時(shí)間格式和目標(biāo)時(shí)間格式不同),我又重新自定義了一個(gè)時(shí)間解析的注解,基本仿照官方的 @Format 注解,具體代碼如下

自定義時(shí)間解析注解:

import com.fasterxml.jackson.annotation.JacksonAnnotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Locale;
import java.util.TimeZone;
/**
 * @author 無(wú)名小生 Date: 2019-02-21 Time: 11:03
 * @version $Id$
 */
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
    ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface DeserializeFormat {
    /**
     * Value that indicates that default {@link java.util.Locale}
     * (from deserialization or serialization context) should be used:
     * annotation does not define value to use.
     */
    public final static String DEFAULT_LOCALE = "##default";
    /**
     * Value that indicates that default {@link java.util.TimeZone}
     * (from deserialization or serialization context) should be used:
     * annotation does not define value to use.
     */
    public final static String DEFAULT_TIMEZONE = "##default";
    /**
     * 按照特定的時(shí)間格式解析
     */
    public String pattern() default "";
    /**
     * 目標(biāo)格式
     * @return
     */
    public String format() default "";
    /**
     * Structure to use for serialization: definition of mapping depends on datatype,
     * but usually has straight-forward counterpart in data format (JSON).
     * Note that commonly only a subset of shapes is available; and if 'invalid' value
     * is chosen, defaults are usually used.
     */
    public DeserializeFormat.Shape shape() default DeserializeFormat.Shape.ANY;
    /**
     * {@link java.util.Locale} to use for serialization (if needed).
     * Special value of {@link #DEFAULT_LOCALE}
     * can be used to mean "just use the default", where default is specified
     * by the serialization context, which in turn defaults to system
     * defaults ({@link java.util.Locale#getDefault()}) unless explicitly
     * set to another locale.
     */
    public String locale() default DEFAULT_LOCALE;
    /**
     * {@link java.util.TimeZone} to use for serialization (if needed).
     * Special value of {@link #DEFAULT_TIMEZONE}
     * can be used to mean "just use the default", where default is specified
     * by the serialization context, which in turn defaults to system
     * defaults ({@link java.util.TimeZone#getDefault()}) unless explicitly
     * set to another locale.
     */
    public String timezone() default DEFAULT_TIMEZONE;
    /*
    /**********************************************************
    /* Value enumeration(s), value class(es)
    /**********************************************************
     */
    /**
     * Value enumeration used for indicating preferred Shape; translates
     * loosely to JSON types, with some extra values to indicate less precise
     * choices (i.e. allowing one of multiple actual shapes)
     */
    public enum Shape
    {
        /**
         * Marker enum value that indicates "default" (or "whatever") choice; needed
         * since Annotations can not have null values for enums.
         */
        ANY,
        /**
         * Value that indicates shape should not be structural (that is, not
         * {@link #ARRAY} or {@link #OBJECT}, but can be any other shape.
         */
        SCALAR,
        /**
         * Value that indicates that (JSON) Array type should be used.
         */
        ARRAY,
        /**
         * Value that indicates that (JSON) Object type should be used.
         */
        OBJECT,
        /**
         * Value that indicates that a numeric (JSON) type should be used
         * (but does not specify whether integer or floating-point representation
         * should be used)
         */
        NUMBER,
        /**
         * Value that indicates that floating-point numeric type should be used
         */
        NUMBER_FLOAT,
        /**
         * Value that indicates that integer number type should be used
         * (and not {@link #NUMBER_FLOAT}).
         */
        NUMBER_INT,
        /**
         * Value that indicates that (JSON) String type should be used.
         */
        STRING,
        /**
         * Value that indicates that (JSON) boolean type
         * (true, false) should be used.
         */
        BOOLEAN
        ;
        public boolean isNumeric() {
            return (this == NUMBER) || (this == NUMBER_INT) || (this == NUMBER_FLOAT);
        }
        public boolean isStructured() {
            return (this == OBJECT) || (this == ARRAY);
        }
    }
    /**
     * Helper class used to contain information from a single {@link DeserializeFormat}
     * annotation.
     */
    public static class Value
    {
        private final String pattern;
        private final String format;
        private final DeserializeFormat.Shape shape;
        private final Locale locale;
        private final String timezoneStr;
        // lazily constructed when created from annotations
        private TimeZone _timezone;
        public Value() {
            this("", "", DeserializeFormat.Shape.ANY, "", "");
        }
        public Value(DeserializeFormat ann) {
            this(ann.pattern(),ann.format(), ann.shape(), ann.locale(), ann.timezone());
        }
        public Value(String p, String f, DeserializeFormat.Shape sh, String localeStr, String tzStr)
        {
            this(p,f, sh,
                 (localeStr == null || localeStr.length() == 0 || DEFAULT_LOCALE.equals(localeStr)) ?
                     null : new Locale(localeStr),
                 (tzStr == null || tzStr.length() == 0 || DEFAULT_TIMEZONE.equals(tzStr)) ?
                     null : tzStr,
                 null
            );
        }
        /**
         * @since 2.1
         */
        public Value(String p, String f, DeserializeFormat.Shape sh, Locale l, TimeZone tz)
        {
            pattern = p;
            format = f;
            shape = (sh == null) ? DeserializeFormat.Shape.ANY : sh;
            locale = l;
            _timezone = tz;
            timezoneStr = null;
        }
        /**
         * @since 2.4
         */
        public Value(String p, String f, DeserializeFormat.Shape sh, Locale l, String tzStr, TimeZone tz)
        {
            pattern = p;
            format = f;
            shape = (sh == null) ? DeserializeFormat.Shape.ANY : sh;
            locale = l;
            _timezone = tz;
            timezoneStr = tzStr;
        }
        /**
         * @since 2.1
         */
        public DeserializeFormat.Value withPattern(String p,String f) {
            return new DeserializeFormat.Value(p, f, shape, locale, timezoneStr, _timezone);
        }
        /**
         * @since 2.1
         */
        public DeserializeFormat.Value withShape(DeserializeFormat.Shape s) {
            return new DeserializeFormat.Value(pattern, format, s, locale, timezoneStr, _timezone);
        }
        /**
         * @since 2.1
         */
        public DeserializeFormat.Value withLocale(Locale l) {
            return new DeserializeFormat.Value(pattern, format, shape, l, timezoneStr, _timezone);
        }
        /**
         * @since 2.1
         */
        public DeserializeFormat.Value withTimeZone(TimeZone tz) {
            return new DeserializeFormat.Value(pattern, format, shape, locale, null, tz);
        }
        public String getPattern() { return pattern; }
        public String getFormat() { return format; }
        public DeserializeFormat.Shape getShape() { return shape; }
        public Locale getLocale() { return locale; }
        /**
         * Alternate access (compared to {@link #getTimeZone()}) which is useful
         * when caller just wants time zone id to convert, but not as JDK
         * provided {@link TimeZone}
         *
         * @since 2.4
         */
        public String timeZoneAsString() {
            if (_timezone != null) {
                return _timezone.getID();
            }
            return timezoneStr;
        }
        public TimeZone getTimeZone() {
            TimeZone tz = _timezone;
            if (tz == null) {
                if (timezoneStr == null) {
                    return null;
                }
                tz = TimeZone.getTimeZone(timezoneStr);
                _timezone = tz;
            }
            return tz;
        }
        /**
         * @since 2.4
         */
        public boolean hasShape() { return shape != DeserializeFormat.Shape.ANY; }
        /**
         * @since 2.4
         */
        public boolean hasPattern() {
            return (pattern != null) && (pattern.length() > 0);
        }
        /**
         * @since 2.4
         */
        public boolean hasFormat() {
            return (format != null) && (format.length() > 0);
        }
        /**
         * @since 2.4
         */
        public boolean hasLocale() { return locale != null; }
        /**
         * @since 2.4
         */
        public boolean hasTimeZone() {
            return (_timezone != null) || (timezoneStr != null && !timezoneStr.isEmpty());
        }
    }
}

使用自定義解析注解的時(shí)間解析器

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import com.google.common.collect.Lists;
import com.tujia.rba.framework.core.remote.api.BizErrorCode;
import com.tujia.rba.framework.core.remote.api.BizException;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @author 無(wú)名小生 Date: 2019-02-19 Time: 19:00
 * @version $Id$
 */
public class DateJsonDeserializer extends JsonDeserializer<Date> implements ContextualDeserializer {
    private final static Logger logger = LoggerFactory.getLogger(DateJsonDeserializer.class);
    private final static List<String> FORMATS = Lists
        .newArrayList("yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyyMMdd-HHmmss", "yyyy-MM-dd", "MM-dd", "HH:mm:ss",
                      "yyyy-MM");
    public final DateFormat df;
    public final String formatString;
    public DateJsonDeserializer() {
        this.df = null;
        this.formatString = null;
    }
    public DateJsonDeserializer(DateFormat df) {
        this.df = df;
        this.formatString = "";
    }
    public DateJsonDeserializer(DateFormat df, String formatString) {
        this.df = df;
        this.formatString = formatString;
    }
    @Override
    public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        try {
            String dateValue = p.getText();
            if (df == null || StringUtils.isEmpty(dateValue)) {
                return null;
            }
            Date date;
            if (StringUtils.isNumeric(dateValue)){
                date = new Date(Long.valueOf(dateValue));
            }else {
                String[] formatArray = FORMATS.toArray(new String[0]);
                date = DateUtils.parseDate(p.getText(),formatArray);
            }
            return df.parse(df.format(date));
        } catch (ParseException | SecurityException e) {
            logger.error("JSON反序列化,時(shí)間解析失敗", e);
            throw new BizException(BizErrorCode.UNEXPECTED_ERROR);
        }
    }
    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
        if (property != null) {
//            JsonFormat.Value format = ctxt.getAnnotationIntrospector().findFormat(property.getMember());
            DeserializeFormat deFormat = property.getAnnotation(DeserializeFormat.class);
            DeserializeFormat.Value format = (deFormat == null)  ? null : new DeserializeFormat.Value(deFormat);
            if (format != null) {
                TimeZone tz = format.getTimeZone();
                // First: fully custom pattern?
                if (format.hasPattern() && !FORMATS.contains(format.getPattern())){
                    FORMATS.add(format.getPattern());
                }
                if (format.hasFormat()) {
                    final String dateFormat = format.getFormat();
                    final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
                    SimpleDateFormat df = new SimpleDateFormat(dateFormat, loc);
                    if (tz == null) {
                        tz = ctxt.getTimeZone();
                    }
                    df.setTimeZone(tz);
                    return new DateJsonDeserializer(df, dateFormat);
                }
                // But if not, can still override timezone
                if (tz != null) {
                    DateFormat df = ctxt.getConfig().getDateFormat();
                    // one shortcut: with our custom format, can simplify handling a bit
                    if (df.getClass() == StdDateFormat.class) {
                        final Locale loc = format.hasLocale() ? format.getLocale() : ctxt.getLocale();
                        StdDateFormat std = (StdDateFormat) df;
                        std = std.withTimeZone(tz);
                        std = std.withLocale(loc);
                        df = std;
                    } else {
                        // otherwise need to clone, re-set timezone:
                        df = (DateFormat) df.clone();
                        df.setTimeZone(tz);
                    }
                    return new DateJsonDeserializer(df);
                }
            }
        }
        return this;
    }
}

@JsonFormat的使用

實(shí)體類(lèi)字段中添加@JsonFormat注解(),返回 yyyy-MM-dd  HH:mm:ss  時(shí)間格式

@JsonFormat(pattern = "yyyy-MM-dd  HH:mm:ss", timezone = "GMT+8")
private Date joinedDate;

pattern:日期格式

timezone:時(shí)區(qū)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Cookie 實(shí)現(xiàn)的原理

    Cookie 實(shí)現(xiàn)的原理

    我們?cè)跒g覽器中,經(jīng)常涉及到數(shù)據(jù)的交換,比如你登錄郵箱,登錄一個(gè)頁(yè)面。我們經(jīng)常會(huì)在此時(shí)設(shè)置30天內(nèi)記住我,或者自動(dòng)登錄選項(xiàng)。那么它們是怎么記錄信息的呢,答案就是今天的主角cookie了
    2021-06-06
  • spring?cloud之eureka高可用集群和服務(wù)分區(qū)解析

    spring?cloud之eureka高可用集群和服務(wù)分區(qū)解析

    這篇文章主要介紹了spring?cloud之eureka高可用集群和服務(wù)分區(qū)解析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java實(shí)現(xiàn)商城訂單超時(shí)取消功能

    Java實(shí)現(xiàn)商城訂單超時(shí)取消功能

    大多數(shù)的B2C商城項(xiàng)目都會(huì)有限時(shí)活動(dòng),當(dāng)用戶下單后都會(huì)有支付超時(shí)時(shí)間,當(dāng)訂單超時(shí)后訂單的狀態(tài)就會(huì)自動(dòng)變成已取消 ,這個(gè)功能的實(shí)現(xiàn)有很多種方法,本文的實(shí)現(xiàn)方法適合大多數(shù)比較小的商城使用。具體實(shí)現(xiàn)方式可以跟隨小編一起看看吧
    2019-12-12
  • Springboot啟動(dòng)流程詳細(xì)分析

    Springboot啟動(dòng)流程詳細(xì)分析

    這篇文章主要介紹了SpringBoot啟動(dòng)過(guò)程的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-12-12
  • JVM內(nèi)存模型知識(shí)點(diǎn)總結(jié)

    JVM內(nèi)存模型知識(shí)點(diǎn)總結(jié)

    在本篇文章里小編給大家分享了關(guān)于JVM內(nèi)存模型的學(xué)習(xí)心得以及相關(guān)知識(shí)點(diǎn)總結(jié),有興趣的朋友們跟著學(xué)習(xí)下。
    2019-05-05
  • 淺聊一下Spring中Bean的配置細(xì)節(jié)

    淺聊一下Spring中Bean的配置細(xì)節(jié)

    我們知道,當(dāng)寫(xiě)完一個(gè)普通的 Java 類(lèi)后,想讓 Spring IoC 容器在創(chuàng)建類(lèi)的實(shí)例對(duì)象時(shí)使用構(gòu)造方法完成實(shí)例對(duì)象的依賴(lài)注入,那么就需要在配置元數(shù)據(jù)中寫(xiě)好類(lèi)的 Bean 定義,包括各種標(biāo)簽的屬性。所以本文我們來(lái)說(shuō)說(shuō)這其中的配置細(xì)節(jié),需要的朋友可以參考下
    2023-07-07
  • Java如何獲取屬性的注釋信息詳解

    Java如何獲取屬性的注釋信息詳解

    Java注解是從Java5開(kāi)始添加到Java的,這篇文章主要給大家介紹了關(guān)于Java如何獲取屬性的注釋信息的相關(guān)資料,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2021-07-07
  • Java?BigDecimal類(lèi)的一般使用、BigDecimal轉(zhuǎn)double方式

    Java?BigDecimal類(lèi)的一般使用、BigDecimal轉(zhuǎn)double方式

    這篇文章主要介紹了Java?BigDecimal類(lèi)的一般使用、BigDecimal轉(zhuǎn)double方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • 基于Java實(shí)現(xiàn)連連看游戲的示例代碼

    基于Java實(shí)現(xiàn)連連看游戲的示例代碼

    連連看游戲顧名思義就是找出具有關(guān)聯(lián)關(guān)系的事物并進(jìn)行相應(yīng)處理。本文將用java語(yǔ)言實(shí)現(xiàn)這一經(jīng)典游戲,采用了swing技術(shù)進(jìn)行了界面化處理,感興趣的可以了解一下
    2022-09-09
  • Java如何獲取word文檔的條目化內(nèi)容

    Java如何獲取word文檔的條目化內(nèi)容

    這篇文章主要介紹了Java獲取word文檔的條目化內(nèi)容的相關(guān)知識(shí),非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2018-05-05

最新評(píng)論

信宜市| 长泰县| 华蓥市| 资溪县| 白河县| 彝良县| 西平县| 凤凰县| 郁南县| 田林县| 社旗县| 惠来县| 龙海市| 资兴市| 衡水市| 丹寨县| 宜黄县| 于田县| 封丘县| 体育| 会理县| 松阳县| 旬阳县| 溧水县| 余庆县| 城市| 林州市| 科尔| 那坡县| 阿克| 开封县| 施秉县| 科技| 江山市| 安丘市| 杂多县| 兴隆县| 新巴尔虎右旗| 安顺市| 顺平县| 菏泽市|