Java項目實戰(zhàn)開發(fā)Day18 2020-04-14

命令行參數(shù)項目

看完老師寫的,我發(fā)現(xiàn)我的代碼真的還是弱爆了。多說無益,下面我吸取了一些經(jīng)驗,來記錄一下。

一.缺點

1.無架構(gòu)意識

目前來說,我自己寫的時候都是想起什么寫什么,根本沒有架構(gòu)的意識。所謂架構(gòu)意識,通俗一點就是:
自己先畫一個圖,把程序的結(jié)構(gòu)理清楚。再去一個一個地填充。

實現(xiàn)命令行工具UML.png

上圖是老師對這個程序的架構(gòu)。

2.基本沒有用接口的意識

①其實可以用接口先規(guī)定一個框架,也就是都有什么方法,具體實現(xiàn)的話,再創(chuàng)建一個實現(xiàn)類來實現(xiàn)即可。但是我在寫的時候根本沒有這個意識。
②除了規(guī)定框架,接口還可以保存各種各樣的常量,別忘了接口默認(rèn)是被public static final修飾的。
③在寫實現(xiàn)類時,可以先提供一個方法的空殼,而不是說寫出一個方法,就必須要把它的所有執(zhí)行內(nèi)容寫出來。

3.想的情況不全面,寫的代碼不高級

①所謂“想的情況不全面”
就是有些情況我根本沒有想到。比如輸入ls,之后緊跟著要判斷一下總的參數(shù)的個數(shù),如果是1,那就對了,因為ls就是1個參數(shù),如果你輸入 ls 然后又手誤,又連帶了一個a,那么這個時候就應(yīng)該提示一下,輸入?yún)?shù)有誤,請重新輸入。這只是一個簡單的例子。
②所謂“寫的代碼不高級”
一個是使用的類不靈活,比如我可能只會想到用String,但是可能用StringBuilder更方便一些,也更高級一些,但是我想不起來用這個。還有一個是代碼結(jié)構(gòu)不高級,所謂高級,并不是說結(jié)構(gòu)亂套難懂,而是應(yīng)該盡可能減少代碼之間的關(guān)聯(lián)度,使用一些操作使程序效率更高等等。比如老師用到了數(shù)據(jù)回調(diào)(后面還會講),但是我數(shù)據(jù)回調(diào)還沒有完全理解,今天花了半個小時查閱了很多資料才大概懂了。所以最后還要復(fù)習(xí)一下自己所有的Java博客,復(fù)習(xí)一下基礎(chǔ)知識。

4.掌握的類的方法太少

有些類的方法是很常見也很實用的,但是我就是想不起來用,可能還是寫的代碼太少。比如Arrays.asList()可以把Array類型轉(zhuǎn)換為List類型StringBuilder可以在后面追加字符串,也可以刪除字符串,還可以找到指定字符串的索引值等等,這些方法,我都沒有掌握。

5.無自定義異常類的意識

之所以沒這個意識,我認(rèn)為還是操作不熟練。比如自定義異常類要進(jìn)行一系列拋出并捕獲,還要super(s),這些雖然學(xué)過,但是我基本沒用過,所以很快就忘了。

二.具體代碼

①ICmd接口

用來保存常量

package interfaces;

//定義指令
public interface ICmd {
   //接口里面的變量默認(rèn)被public static final修飾 
   String LS = "ls";          //列出當(dāng)前目錄的所有內(nèi)容
   String MKDIR = "mkdir";    //創(chuàng)建目錄
   String COPY = "copy";      //拷貝
   String RM = "rm";          //刪除
   String CD = "cd";          //進(jìn)入一個目錄
   String CDP = "cd..";       //進(jìn)入上一層目錄
   
   String[] COMMONDS = new String[] {LS,MKDIR,COPY,RM,CD,CDP};
}

②ICommond接口

用來定義方法框架

package interfaces;

//規(guī)劃一下執(zhí)行的操作
public interface ICommond {
   //默認(rèn) public abstract修飾
    boolean list();               //列出當(dāng)前目錄的所有內(nèi)容 名字+size
    
    boolean mkdir(String path);              //創(chuàng)建一個目錄
    
    boolean copy(String src,String des);     //將src的文件復(fù)制到des位置
    
    boolean remove(String path);             //刪除文件
    
    boolean cd_to_child(String path);        //切換當(dāng)前目錄到子目錄/進(jìn)入子目錄
    
    boolean cd_to_parent();                  //切換當(dāng)前目錄到上一層目錄
}

③MyClass類

程序入口

package Main;

import Tool.CommondTool;

public class MyClass {
     public static void main(String[] args) {
         
         CommondTool tool = new CommondTool();
         
         tool.start();
     }
}

④CommondOperation類

用來執(zhí)行某些外包操作與回調(diào)

package Tool;

import java.util.*;

import interfaces.*;

//接收用戶輸入和解析輸入類型
public class CommondOperation {
   //回調(diào)對象
   private ICommond listener;
   //保存所有指令
   private List<String> commonds;
   //獲取輸入信息
   private Scanner mScanner;
   public CommondOperation() {
       mScanner = new Scanner(System.in);
       //將普通的Array類型轉(zhuǎn)化為List
       commonds = Arrays.asList(ICmd.COMMONDS);
   }
   
   
   
   //接收用戶輸入的指令
   public void readCommond(ICommond listener)throws PXDException.CommondNotExistException,PXDException.CommondArgumentErrorException  {
        this.listener = listener;
        
        //接收指令
        String commond = mScanner.nextLine();//記住這里用nextLine()
    
        //解析指令
        parseCommond(commond);
    }
   
    //解析指令
    public void parseCommond(String commond)throws PXDException.CommondNotExistException,PXDException.CommondArgumentErrorException {
        //將指令以空格為分割符 分開
        String[] componts = commond.split(" ");//這也是當(dāng)時焦頭爛額的地方
        
        //獲取用戶指令
        String cmd = componts[0];
            
        //判斷指令是否存在
        if(!commonds.contains(cmd)) {
            //輸入指令不存在
            //需要拋出異常
            throw new PXDException.CommondNotExistException("指令不存在");
        }
        //存在就解析是哪種指令
        switch(cmd) {
        case ICmd.CD:
            //進(jìn)入某一個子目錄
            if(componts.length != 2) {
                //參數(shù)不正確,應(yīng)該只有兩個
                throw new PXDException.CommondArgumentErrorException("cd 參數(shù)不正確");
            }
            listener.cd_to_child(componts[1]);
            break;
        case ICmd.CDP:
            //進(jìn)入父目錄
            if(componts.length != 1) {
                //參數(shù)不正確,應(yīng)該只有兩個
                throw new PXDException.CommondArgumentErrorException("cd.. 參數(shù)不正確,它不需要參數(shù)");
            }
            listener.cd_to_parent();
            break;
        case ICmd.MKDIR:
            //創(chuàng)建目錄
            if(componts.length != 2) {
                //參數(shù)不正確
                throw new PXDException.CommondArgumentErrorException("mkdir 參數(shù)不正確");
            }
            listener.mkdir(componts[1]);
            break;
        case ICmd.RM:
            //刪除
            if(componts.length != 2) {
                //參數(shù)不正確
                throw new PXDException.CommondArgumentErrorException("rm 參數(shù)不正確");
            }
            listener.remove(componts[1]);
            break;
        case ICmd.LS:
            //列
            if(componts.length != 1) {
                //參數(shù)不正確
                throw new PXDException.CommondArgumentErrorException("ls不需要參數(shù)");
            }
            listener.list();
            break;
        case ICmd.COPY:
            if(componts.length != 3) {
                //參數(shù)不正確
                throw new PXDException.CommondArgumentErrorException("copy參數(shù)不正確");
            }
            listener.copy(componts[1], componts[2]);
            break;
        }
    }

}

⑤CommondTool類

繼承框架接口,并執(zhí)行具體操作

package Tool;

import java.io.File;

import Tool.PXDException.CommondNotExistException;
import interfaces.*;

//接收用戶的指令 和 處理指令
public class CommondTool implements ICommond{
    //默認(rèn)啟動起來操作的目錄
    private static final String DESKTOP_PATH = "C:\\Users\\劉金豪\\Desktop";//桌面
    
    //記錄當(dāng)前操作的目錄路徑
    private StringBuilder currentDirPath;
    
    //構(gòu)造方法
    public CommondTool() {
        currentDirPath = new StringBuilder(DESKTOP_PATH);
    }
    
    //啟動命令行工具
    public void start() {
        //創(chuàng)建讀取指令的類的對象
        CommondOperation operation = new CommondOperation();
        //歡迎界面
        System.out.println("歡迎用戶使用命令行工具!!");
        while(true) {
            //顯示當(dāng)前路徑(只需要提示當(dāng)前路徑的最后部分)
            showParent();
            
            //輸入指令
            try {
                operation.readCommond(this);
            } catch (Exception e) {
                System.out.println(e.getMessage());//熟悉一下getMessage方法的使用
            }
        }
        
    }

    private void showParent() {
        //獲取最后一個\\的index
        int start = currentDirPath.lastIndexOf("\\");
        //獲取最后的內(nèi)容
        String parent = currentDirPath.substring(start);//獲取子字符串
        //輸出提示內(nèi)容
        System.out.print(parent+"#");
    }
    
    @Override
    public boolean list() {
        File[]files = FileManager.getInstance().list(currentDirPath.toString());//注意轉(zhuǎn)化成字符串類型
        for(File file:files) {
            //獲取文件名
            String name = file.getName();
            //獲取文件長度
            long size = file.length();
            long kb = size/1024;
            long by = size%1024;
            System.out.print(name +" " );
            for(int i = 0;i < 40 - name.length();i++) {
                System.out.print(" ");
            }
            System.out.println(kb + "." + by + "kb");
            
         }
         return false;  
}  
        
    

    @Override
    public boolean mkdir(String path) {
        //拼接完整路徑
        String dirPath = currentDirPath.toString() + "\\" + path;
        FileManager.getInstance().mkdir(dirPath);
        return false;
    }

    @Override
    public boolean copy(String src, String des) {
        //拼接完整路徑
        String srcPath = currentDirPath.toString() + "\\" + src;
        String desPath = currentDirPath.toString() + "\\" + des;
        
        FileManager.getInstance().copy(srcPath, desPath);
        return false;
    }

    @Override
    public boolean remove(String path) {
        //拼接完整路徑
        String dirPath = currentDirPath.toString() + "\\" + path;
        FileManager.getInstance().remove(dirPath);
        return false;
    }

    @Override
    public boolean cd_to_child(String path) {
        currentDirPath.append("\\" + path);
        System.out.println("進(jìn)入子目錄:"+path);
        return false;
    }

    @Override
    public boolean cd_to_parent() {
        //先轉(zhuǎn)換成字符串
        int start = currentDirPath.toString().lastIndexOf("\\");
        int end = currentDirPath.length();//復(fù)習(xí)一下,這個end到底刪不刪除
        currentDirPath.delete(start, end);
        return false;
    }
}

⑥FileManager類

用來執(zhí)行更具體的操作,因為此程序基本所有操作都是涉及文件的,所以專門用一個類來封裝操作。

package Tool;

import java.io.*;

public class FileManager {
    //單例模式
   private static FileManager manager;
   private FileManager() {
       
   }
    
   public static FileManager getInstance() {
        if(manager == null) {
            synchronized(FileManager.class) {
                if(manager == null) {
                    manager = new FileManager();
                }
            }
        }
        return manager;
    }
   
   //創(chuàng)建目錄
   public boolean mkdir(String path) {
       File file = new File(path);
       if(file.exists()) {
           return false;
       }
       return file.mkdir();//mkdir返回值就是boolean類型
       //mkdir要求路徑中的目錄都存在
       //mkdirs如果路徑中的目錄不存在,也會自動創(chuàng)建
   }
   
   //刪除文件或目錄
   public boolean remove(String path) {
       File file = new File(path);
       if(!file.exists()) {
           return false;
       }
       
       return file.delete();
   }
   
   //列出所有文件信息
   public File[] list(String path){
       File file = new File(path);
       if(!file.exists()) {
           return null;
       }
       return file.listFiles(new FilenameFilter() {
        
           //名字是點開頭的都是隱藏文件,這些文件我不想要。就篩選一下
           public boolean accept(File file, String s) {
            if(s.startsWith(".")) {
                return false;
            }
            return true;
        }
           
       });
   }
   
   //文件或者目錄的拷貝
   public boolean copy(String src,String des) {
       File srcFile = new File(src);
       File desFile = new File(des);
       
       //判斷文件是否存在
       if(!srcFile.exists() || desFile.exists()) {//注意這里的細(xì)節(jié)
           return false;
       }
       
       //如果是文件
       if(srcFile.isFile()) {
           //直接拷貝文件
           copyFile(src,des);
       }else {
           //是目錄
           //創(chuàng)建當(dāng)前的目錄
           desFile.mkdir();
           
           //需要將源目錄的所有內(nèi)容拷貝到des目錄
           for(File file:srcFile.listFiles()) {
               copy(file.getAbsolutePath(),des + "\\" + file.getName());//注意這里是一個高級的地方,用到了嵌套
               //因為目錄里面可能還有目錄。
           }
       }
       
       return true;
   }
   
   private void copyFile(String src,String des) {
        
    try (FileInputStream fis = new FileInputStream(src);
        BufferedInputStream bis = new BufferedInputStream(fis);
        
        //創(chuàng)建輸出流
        FileOutputStream fos = new FileOutputStream(des);
        BufferedOutputStream bos = new BufferedOutputStream(fos);){
        
        //創(chuàng)建buffer
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = bis.read()) != -1) {
                bos.write(buffer);
        }
        bos.flush();
    } catch (Exception e) {
        e.printStackTrace();
    }
    //也可以使用finally關(guān)閉,那么先關(guān)閉后面創(chuàng)建的,再關(guān)閉前面創(chuàng)建的
    
       
   }
}

⑦PXDException類

自定義異常類

package Tool;

//自定義異常
public class PXDException {
    //指令不存在
    static class CommondNotExistException extends Exception{
        public CommondNotExistException(String s) {
            super(s);
        }
    }
    //參數(shù)不正確
    static class CommondArgumentErrorException extends Exception{
        public CommondArgumentErrorException(String s) {
            super(s);
        }
    }
}

三.知識漏洞

1.自定義異常類使用不熟練

具體講解在:http://www.lxweimin.com/p/818ea803a092
主要是注意拋出和捕獲,以及super()方法的使用。

2.回調(diào)機制沒理解

關(guān)于回調(diào)機制,網(wǎng)上有一篇非常形象的文章:https://www.cnblogs.com/heshuchao/p/5376298.html
就是A繼承了接口I,然后A調(diào)用B類的一個方法,把自己這個對象this傳過去,然后B類用I類型接收,再用I點的方式,回調(diào)A的方法(因為A實現(xiàn)了I,調(diào)用I的那個方法,就是調(diào)用A的相應(yīng)方法),并把執(zhí)行結(jié)果作為參數(shù)傳遞給A的那個方法,這就是我現(xiàn)在理解的數(shù)據(jù)回調(diào)。
我也是查了好久,才懂了個大概,我盡量去理解,不過也可能礙于現(xiàn)在寫的代碼太少,可能理解深還是說不上。

3.文件篩選函數(shù)不會使用

具體講解在:http://www.lxweimin.com/p/ceb25797c109
主要是要new FilenameFilte這個r接口,并實現(xiàn)相應(yīng)方法,這一步操作我不熟悉

4.字符串類型方法沒掌握

當(dāng)時講課的時候介紹了大量的方法,還是沒有完全掌握。比如StringBuilder如何轉(zhuǎn)換為String類型啊,或者是在StringBuilder類型后面追加字符串呀,怎么樣獲取索引值等等等等,如何把普通數(shù)組類型轉(zhuǎn)換為List類型等等(當(dāng)然這個不屬于字符串)

四.總結(jié)

雖然看到自己還有非常多的不足,但是發(fā)現(xiàn)的不足多,進(jìn)步才能多嘛!畢竟現(xiàn)在寫的項目還是太少。我要吸取這次的經(jīng)驗,爭取下一次項目都不再犯同樣失誤。加油吧!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。