Java實(shí)現(xiàn)FTP服務(wù)器功能實(shí)例代碼
FTP(File Transfer Protocol 文件傳輸協(xié)議)是Internet 上用來傳送文件的協(xié)議。在Internet上通過FTP 服務(wù)器可以進(jìn)行文件的上傳(Upload)或下載(Download)。FTP是實(shí)時(shí)聯(lián)機(jī)服務(wù),在使用它之前必須是具有該服務(wù)的一個(gè)用戶(用戶名和口令),工作時(shí)客戶端必須先登錄到作為服務(wù)器一方的計(jì)算機(jī)上,用戶登錄后可以進(jìn)行文件搜索和文件傳送等有關(guān)操作,如改變當(dāng)前工作目錄、列文件目錄、設(shè)置傳輸參數(shù)及傳送文件等。使用FTP可以傳送所有類型的文件,如文本文件、二進(jìn)制可執(zhí)行文件、圖象文件、聲音文件和數(shù)據(jù)壓縮文件等。
FTP 命令
FTP 的主要操作都是基于各種命令基礎(chǔ)之上的。常用的命令有:
設(shè)置傳輸模式,它包括ASCⅡ(文本) 和BINARY 二進(jìn)制模式;
目錄操作,改變或顯示遠(yuǎn)程計(jì)算機(jī)的當(dāng)前目錄(cd、dir/ls 命令);
連接操作,open命令用于建立同遠(yuǎn)程計(jì)算機(jī)的連接;close命令用于關(guān)閉連接;
發(fā)送操作,put命令用于傳送文件到遠(yuǎn)程計(jì)算機(jī);mput 命令用于傳送多個(gè)文件到遠(yuǎn)程計(jì)算機(jī);
獲取操作,get命令用于接收一個(gè)文件;mget命令用于接收多個(gè)文件。
import java.net.Socket; import org.apache.log4j.Logger; /** * 角色——服務(wù)器A * @author Leon * */ public class ServerA{ public static void main(String[] args){ final String F_DIR = "c:/test";//根路徑 final int PORT = 22;//監(jiān)聽端口號(hào) Logger.getRootLogger(); Logger logger = Logger.getLogger("com"); try{ ServerSocket s = new ServerSocket(PORT); logger.info("Connecting to server A..."); logger.info("Connected Successful! Local Port:"+s.getLocalPort()+". Default Directory:'"+F_DIR+"'."); while( true ){ //接受客戶端請(qǐng)求 Socket client = s.accept(); //創(chuàng)建服務(wù)線程 new ClientThread(client, F_DIR).start(); } } catch(Exception e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } } import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.net.ConnectException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.nio.charset.Charset; import java.util.Random; import org.apache.log4j.Logger; /** * 客戶端子線程類 * @author Leon * */ public class ClientThread extends Thread { private Socket socketClient;//客戶端socket private Logger logger;//日志對(duì)象 private String dir;//絕對(duì)路徑 private String pdir = "/";//相對(duì)路徑 private final static Random generator = new Random();//隨機(jī)數(shù) public ClientThread(Socket client, String F_DIR){ this.socketClient = client; this.dir = F_DIR; } @Override public void run() { Logger.getRootLogger(); logger = Logger.getLogger("com"); InputStream is = null; OutputStream os = null; try { is = socketClient.getInputStream(); os = socketClient.getOutputStream(); } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } BufferedReader br = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); PrintWriter pw = new PrintWriter(os); String clientIp = socketClient.getInetAddress().toString().substring(1);//記錄客戶端IP String username = "not logged in";//用戶名 String password = "";//口令 String command = "";//命令 boolean loginStuts = false;//登錄狀態(tài) final String LOGIN_WARNING = "530 Please log in with USER and PASS first."; String str = "";//命令內(nèi)容字符串 int port_high = 0; int port_low = 0; String retr_ip = "";//接收文件的IP地址 Socket tempsocket = null; //打印歡迎信息 pw.println("220-FTP Server A version 1.0 written by Leon Guo"); pw.flush(); logger.info("("+username+") ("+clientIp+")> Connected, sending welcome message..."); logger.info("("+username+") ("+clientIp+")> 220-FTP Server A version 1.0 written by Leon Guo"); boolean b = true; while ( b ){ try { //獲取用戶輸入的命令 command = br.readLine(); if(null == command) break; } catch (IOException e) { pw.println("331 Failed to get command"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 331 Failed to get command"); logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } b = false; } /* * 訪問控制命令 */ // USER命令 if(command.toUpperCase().startsWith("USER")){ logger.info("(not logged in) ("+clientIp+")> "+command); username = command.substring(4).trim(); if("".equals(username)){ pw.println("501 Syntax error"); pw.flush(); logger.info("(not logged in) ("+clientIp+")> 501 Syntax error"); username = "not logged in"; } else{ pw.println("331 Password required for " + username); pw.flush(); logger.info("(not logged in) ("+clientIp+")> 331 Password required for " + username); } loginStuts = false; } //end USER // PASS命令 else if(command.toUpperCase().startsWith("PASS")){ logger.info("(not logged in) ("+clientIp+")> "+command); password = command.substring(4).trim(); if(username.equals("root") && password.equals("root")){ pw.println("230 Logged on"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 230 Logged on"); // logger.info("客戶端 "+clientIp+" 通過 "+username+"用戶登錄"); loginStuts = true; } else{ pw.println("530 Login or password incorrect!"); pw.flush(); logger.info("(not logged in) ("+clientIp+")> 530 Login or password incorrect!"); username = "not logged in"; } } //end PASS // PWD命令 else if(command.toUpperCase().startsWith("PWD")){ logger.info("("+username+") ("+clientIp+")> "+command); if(loginStuts){ // logger.info("用戶"+clientIp+":"+username+"執(zhí)行PWD命令"); pw.println("257 /""+pdir+"/" is current directory"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 257 /""+pdir+"/" is current directory"); } else{ pw.println(LOGIN_WARNING); pw.flush(); logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING); } } //end PWD // CWD命令 else if(command.toUpperCase().startsWith("CWD")){ logger.info("("+username+") ("+clientIp+")> "+command); if(loginStuts){ str = command.substring(3).trim(); if("".equals(str)){ pw.println("250 Broken client detected, missing argument to CWD. /""+pdir+"/" is current directory."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 250 Broken client detected, missing argument to CWD. /""+pdir+"/" is current directory."); } else{ //判斷目錄是否存在 String tmpDir = dir + "/" + str; File file = new File(tmpDir); if(file.exists()){//目錄存在 dir = dir + "/" + str; if("/".equals(pdir)){ pdir = pdir + str; } else{ pdir = pdir + "/" + str; } // logger.info("用戶"+clientIp+":"+username+"執(zhí)行CWD命令"); pw.println("250 CWD successful. /""+pdir+"/" is current directory"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 250 CWD successful. /""+pdir+"/" is current directory"); } else{//目錄不存在 pw.println("550 CWD failed. /""+pdir+"/": directory not found."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 550 CWD failed. /""+pdir+"/": directory not found."); } } } else{ pw.println(LOGIN_WARNING); pw.flush(); logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING); } } //end CWD // QUIT命令 else if(command.toUpperCase().startsWith("QUIT")){ logger.info("("+username+") ("+clientIp+")> "+command); b = false; pw.println("221 Goodbye"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 221 Goodbye"); try { Thread.currentThread(); Thread.sleep(1000); } catch (InterruptedException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } //end QUIT /* * 傳輸參數(shù)命令 */ //PORT命令,主動(dòng)模式傳輸數(shù)據(jù) else if(command.toUpperCase().startsWith("PORT")){ logger.info("("+username+") ("+clientIp+")> "+command); if(loginStuts){ try { str = command.substring(4).trim(); port_low = Integer.parseInt(str.substring(str.lastIndexOf(",")+1)); port_high = Integer.parseInt(str.substring(0, str.lastIndexOf(",")) .substring(str.substring(0, str.lastIndexOf(",")).lastIndexOf(",")+1)); String str1 = str.substring(0, str.substring(0, str.lastIndexOf(",")).lastIndexOf(",")); retr_ip = str1.replace(",", "."); try { //實(shí)例化主動(dòng)模式下的socket tempsocket = new Socket(retr_ip,port_high * 256 + port_low); // logger.info("用戶"+clientIp+":"+username+"執(zhí)行PORT命令"); pw.println("200 port command successful"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 200 port command successful"); } catch (ConnectException ce) { pw.println("425 Can't open data connection."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 425 Can't open data connection."); logger.error(ce.getMessage()); for(StackTraceElement ste : ce.getStackTrace()){ logger.error(ste.toString()); } } catch (UnknownHostException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } catch (NumberFormatException e) { pw.println("503 Bad sequence of commands."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands."); logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } else{ pw.println(LOGIN_WARNING); pw.flush(); logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING); } } //end PORT //PASV命令,被動(dòng)模式傳輸數(shù)據(jù) else if(command.toUpperCase().startsWith("PASV")) { logger.info("("+username+") ("+clientIp+")> "+command); if(loginStuts){ ServerSocket ss = null; while( true ){ //獲取服務(wù)器空閑端口 port_high = 1 + generator.nextInt(20); port_low = 100 + generator.nextInt(1000); try { //服務(wù)器綁定端口 ss = new ServerSocket(port_high * 256 + port_low); break; } catch (IOException e) { continue; } } // logger.info("用戶"+clientIp+":"+username+"執(zhí)行PASV命令"); InetAddress i = null; try { i = InetAddress.getLocalHost(); } catch (UnknownHostException e1) { e1.printStackTrace(); } pw.println("227 Entering Passive Mode ("+i.getHostAddress().replace(".", ",")+","+port_high+","+port_low+")"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 227 Entering Passive Mode ("+i.getHostAddress().replace(".", ",")+","+port_high+","+port_low+")"); try { //被動(dòng)模式下的socket tempsocket = ss.accept(); ss.close(); } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } else{ pw.println(LOGIN_WARNING); pw.flush(); logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING); } } //end PASV //RETR命令 else if(command.toUpperCase().startsWith("RETR")){ logger.info("("+username+") ("+clientIp+")> "+command); if(loginStuts){ str = command.substring(4).trim(); if("".equals(str)){ pw.println("501 Syntax error"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 501 Syntax error"); } else { try { pw.println("150 Opening data channel for file transfer."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 150 Opening data channel for file transfer."); RandomAccessFile outfile = null; OutputStream outsocket = null; try { //創(chuàng)建從中讀取和向其中寫入(可選)的隨機(jī)訪問文件流,該文件具有指定名稱 outfile = new RandomAccessFile(dir+"/"+str,"r"); outsocket = tempsocket.getOutputStream(); } catch (FileNotFoundException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } byte bytebuffer[]= new byte[1024]; int length; try{ while((length = outfile.read(bytebuffer)) != -1){ outsocket.write(bytebuffer, 0, length); } outsocket.close(); outfile.close(); tempsocket.close(); } catch(IOException e){ logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } // logger.info("用戶"+clientIp+":"+username+"執(zhí)行RETR命令"); pw.println("226 Transfer OK"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 226 Transfer OK"); } catch (Exception e){ pw.println("503 Bad sequence of commands."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands."); logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } } else{ pw.println(LOGIN_WARNING); pw.flush(); logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING); } }//end RETR //STOR命令 else if(command.toUpperCase().startsWith("STOR")){ logger.info("("+username+") ("+clientIp+")> "+command); if(loginStuts){ str = command.substring(4).trim(); if("".equals(str)){ pw.println("501 Syntax error"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 501 Syntax error"); } else { try { pw.println("150 Opening data channel for file transfer."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 150 Opening data channel for file transfer."); RandomAccessFile infile = null; InputStream insocket = null; try { infile = new RandomAccessFile(dir+"/"+str,"rw"); insocket = tempsocket.getInputStream(); } catch (FileNotFoundException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } byte bytebuffer[] = new byte[1024]; int length; try{ while((length =insocket.read(bytebuffer) )!= -1){ infile.write(bytebuffer, 0, length); } insocket.close(); infile.close(); tempsocket.close(); } catch(IOException e){ logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } // logger.info("用戶"+clientIp+":"+username+"執(zhí)行STOR命令"); pw.println("226 Transfer OK"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 226 Transfer OK"); } catch (Exception e){ pw.println("503 Bad sequence of commands."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands."); logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } } else { pw.println(LOGIN_WARNING); pw.flush(); logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING); } } //end STOR //NLST命令 else if(command.toUpperCase().startsWith("NLST")) { logger.info("("+username+") ("+clientIp+")> "+command); if(loginStuts){ try { pw.println("150 Opening data channel for directory list."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 150 Opening data channel for directory list."); PrintWriter pwr = null; try { pwr= new PrintWriter(tempsocket.getOutputStream(),true); } catch (IOException e1) { e1.printStackTrace(); } File file = new File(dir); String[] dirstructure = new String[10]; dirstructure= file.list(); for(int i=0;i<dirstructure.length;i++){ pwr.println(dirstructure[i]); } try { tempsocket.close(); pwr.close(); } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } // logger.info("用戶"+clientIp+":"+username+"執(zhí)行NLST命令"); pw.println("226 Transfer OK"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 226 Transfer OK"); } catch (Exception e){ pw.println("503 Bad sequence of commands."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands."); logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } }else{ pw.println(LOGIN_WARNING); pw.flush(); logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING); } } //end NLST //LIST命令 else if(command.toUpperCase().startsWith("LIST")) { logger.info("("+username+") ("+clientIp+")> "+command); if(loginStuts){ try{ pw.println("150 Opening data channel for directory list."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 150 Opening data channel for directory list."); PrintWriter pwr = null; try { pwr= new PrintWriter(tempsocket.getOutputStream(),true); } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } FtpUtil.getDetailList(pwr, dir); try { tempsocket.close(); pwr.close(); } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } // logger.info("用戶"+clientIp+":"+username+"執(zhí)行LIST命令"); pw.println("226 Transfer OK"); pw.flush(); logger.info("("+username+") ("+clientIp+")> 226 Transfer OK"); } catch (Exception e){ pw.println("503 Bad sequence of commands."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 503 Bad sequence of commands."); logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } else { pw.println(LOGIN_WARNING); pw.flush(); logger.info("("+username+") ("+clientIp+")> "+LOGIN_WARNING); } } //end LIST // 輸入非法命令 else{ logger.info("("+username+") ("+clientIp+")> "+command); pw.println("500 Syntax error, command unrecognized."); pw.flush(); logger.info("("+username+") ("+clientIp+")> 500 Syntax error, command unrecognized."); } } //end while try { logger.info("("+username+") ("+clientIp+")> disconnected."); // logger.info("用戶"+clientIp+":"+username+"退出"); br.close(); socketClient.close(); pw.close(); if(null != tempsocket){ tempsocket.close(); } } catch (IOException e) { logger.error(e.getMessage()); for(StackTraceElement ste : e.getStackTrace()){ logger.error(ste.toString()); } } } } import java.io.File; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; /** * FTP工具類 * @author Leon * */ public class FtpUtil { public static void getDetailList(PrintWriter pw, String path){ File dir = new File(path); if (!dir.isDirectory()) { pw.println("500 No such file or directory./r/n"); } File[] files = dir.listFiles(); String modifyDate; for (int i = 0; i < files.length; i++) { modifyDate = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss") .format(new Date(files[i].lastModified())); if (files[i].isDirectory()) { pw.println("drwxr-xr-x ftp ftp 0 " + modifyDate + " " + files[i].getName()); } else { pw.println("-rw-r-r--1 ftp ftp " + files[i].length() + " " + modifyDate + " " + files[i].getName()); } pw.flush(); } pw.println("total:" + files.length); } } ### set log levels ### log4j.logger.com =debug,stdout,D,E ### 輸出到控制臺(tái) ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target=System.out log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n ## 輸出DEBUG級(jí)別以上的日志 log4j.appender.D=org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File=c:/logs/logs.log log4j.appender.D.Append =true ## 輸出DEBUG級(jí)別以上的日志 log4j.appender.D.Threshold=DEBUG log4j.appender.D.layout=org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 保存異常信息到單獨(dú)文件 ### log4j.appender.E=org.apache.log4j.DailyRollingFileAppender ## 異常日志文件名 log4j.appender.E.File=c:/logs/errors.log log4j.appender.E.Append=true ## 只輸出ERROR級(jí)別以上的日志!!! log4j.appender.E.Threshold=ERROR log4j.appender.E.layout=org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
以上內(nèi)容是小編給大家介紹的Java實(shí)現(xiàn)FTP服務(wù)器功能實(shí)例代碼的相關(guān)知識(shí),希望大家喜歡。
相關(guān)文章
Java中實(shí)現(xiàn)List分隔成子List詳解
大家好,本篇文章主要講的是Java中實(shí)現(xiàn)List分隔成子List詳解,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下2022-01-01SpringBoot中@Autowired爆紅原理分析及解決
這篇文章主要介紹了SpringBoot中@Autowired爆紅原理分析及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Java 中DateUtils日期工具類的實(shí)例詳解
這篇文章主要介紹了Java 中DateUtils日期工具類的實(shí)例詳解的相關(guān)資料,有時(shí)候開發(fā)java項(xiàng)目使用日期類型,這里介紹下日期工具類,需要的朋友可以參考下2017-08-08如何在MyBatis中實(shí)現(xiàn)DataSource
今天給大家整理了如何在MyBatis中實(shí)現(xiàn)DataSource,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下2021-06-06IDEA在SpringBoot項(xiàng)目使用Maven打包后jar包太小問題及解決
這篇文章主要介紹了IDEA在SpringBoot項(xiàng)目使用Maven打包后jar包太小問題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04idea2019.1.4 鼠標(biāo)放到方法上顯示注解的實(shí)現(xiàn)操作
這篇文章主要介紹了idea2019.1.4 鼠標(biāo)放到方法上顯示注解的實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-02-02Java設(shè)計(jì)模式編程中的工廠方法模式和抽象工廠模式
這篇文章主要介紹了Java設(shè)計(jì)模式編程中的工廠方法模式和抽象工廠模式,設(shè)計(jì)模式的建立有利于團(tuán)隊(duì)協(xié)作時(shí)代碼的共同維護(hù),需要的朋友可以參考下2016-01-01java中實(shí)體類實(shí)現(xiàn)時(shí)間日期自動(dòng)轉(zhuǎn)換方式
這篇文章主要介紹了java中實(shí)體類實(shí)現(xiàn)時(shí)間日期自動(dòng)轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06