02.制作一個自己的 Java 編輯器


難度中等,適合 Java 基礎扎實,對 Java 核心 API 有所熟悉的同學學習

No1、制作GUI界面

一、實驗介紹

1.1 實驗內容

本節課程的主要內容是准備開發環境,建立項目並完成 GUI 界面的編程實現。

1.2 實驗知識點

Java Swing 編程

1.3 實驗環境

本實驗環境采用帶桌面的 Ubuntu Linux 環境,實驗中會用到的環境或軟件:

JDK1.7

Eclipse。

1.4 適合人群

本節課程難度較低,屬於初級課程,適合要想學習Java Swing 編程的同學學習。

1.5 代碼獲取

你可以在Xfce終端下通過下面命令將實驗的完整工程項目下載到實驗樓環境中,作為參照對比進行學習。

$ wget http://labfile.oss.aliyuncs.com/courses/287/MyEdit.tar.gz

二、項目文件結構 

三、實驗步驟

這一節我們將開發GUI界面。

 3.1 新建項目

首先請雙擊打開桌面上的 Eclipse ,等待它啟動完成后,在菜單 File 中點擊 New -> Java Project選項。 此處輸入圖片的描述 

在彈出的窗口里填寫項目的名稱 MyEdit,然后點擊 Finish 按鈕。

 

 3.2 創建包和類

項目創建完成后,我們需要按照之前的項目結構來創建各個類。本項目一共有兩個類:

FileWindow:主要方法類,用作GUI界面以及邏輯功能的實現

Main:測試類

因此我們首先需要創建一個名為 com.hijackykun.myedit 的包。

 請在創建好的項目目錄 src 文件夾上右鍵點擊,然后選擇 New -> Package。 

在彈出的 New Java Package 對話框中填寫包名com.shiyanlou.myedit,並點擊 Finish 按鈕。 

最后,在新建好的包下新建FileWindow和Main類。

 3.3 GUI 界面的實現

GUI界面的效果圖如下:

 

界面的設計采用卡片布局(CardLayout),白色文本域為程序輸入區,粉紅色文本域為編譯結果顯示區,淺藍色文本域為程序運行結果區。點擊上方不同的功能按鈕顯示對應的文本域。

 在FileWindow類中編寫實現 GUI 的代碼,相關的代碼如下,代碼的講解將會以注釋的形式進行,請在編寫代碼的同時留意相關的注釋。

 注:以下代碼均未導入相關的包

  public class FileWindow extends JFrame implements ActionListener, Runnable {

     /*注意:因為實現了ActionListener 和Runnable接口,所以必須要實現這兩個接口的方法。這里我們先把這兩個方法簡單實現以下。下節課將徹底完成這兩個方法。*/

     Thread compiler = null;

    Thread run_prom = null;

    boolean bn = true;

    CardLayout mycard;  //聲明布局,以后會用到

    File file_saved = null;

    JButton button_input_txt,   //按鈕的定義

            button_compiler_text,

            button_compiler,

            button_run_prom,

            button_see_doswin;

     JPanel p = new JPanel();

    JTextArea input_text = new JTextArea(); // 程序輸入區

    JTextArea compiler_text = new JTextArea();// 編譯錯誤顯示區

    JTextArea dos_out_text = new JTextArea();// 程序的輸出信息

     JTextField input_file_name_text = new JTextField();

    JTextField run_file_name_text = new JTextField();

     public FileWindow() {

        // TODO Auto-generated constructor stub

        super("Java語言編譯器");

        mycard = new CardLayout();

        compiler=new Thread(this);

        run_prom=new Thread(this);

        button_input_txt=new JButton("程序輸入區(白色)");

        button_compiler_text=new JButton("編譯結果區(粉紅色)");

        button_see_doswin=new JButton("程序運行結果(淺藍色)");

        button_compiler=new JButton("編譯程序");

        button_run_prom=new JButton("運行程序");

         p.setLayout(mycard);//設置卡片布局

        p.add("input",input_text);//定義卡片名稱

        p.add("compiler", compiler_text);

        p.add("dos",dos_out_text);

        add(p,"Center");

         compiler_text.setBackground(Color.pink); //設置顏色

        dos_out_text.setBackground(Color.cyan);

        JPanel p1=new JPanel();

         p1.setLayout(new GridLayout(3, 3)); //設置表格布局

        //添加組件

        p1.add(button_input_txt);

        p1.add(button_compiler_text);

        p1.add(button_see_doswin);

        p1.add(new JLabel("輸入編譯文件名(.java):"));

        p1.add(input_file_name_text);

        p1.add(button_compiler);

        p1.add(new JLabel("輸入應用程序主類名"));

        p1.add(run_file_name_text);

        p1.add(button_run_prom);

        add(p1,"North");

         //定義事件

        button_input_txt.addActionListener(this);

        button_compiler.addActionListener(this);

        button_compiler_text.addActionListener(this);

        button_run_prom.addActionListener(this);

        button_see_doswin.addActionListener(this);

    }

     public void actionPerformed(ActionEvent e)

    {

         //實現方法

    }

     @Override

    public void run() {

        //實現方法

    }

    }

 到此,我們的 GUI 界面就算做好了!

   3.4 測試類的實現

下面,我們趕緊做個測試類,測試一下我們的界面。

Main.java:

    import java.awt.event.WindowAdapter;

    import java.awt.event.WindowEvent;

    public class Main {

         public static void main(String[] args) {

            // TODO Auto-generated method stub

            FileWindow win=new FileWindow();

            win.pack();

            win.addWindowListener(new WindowAdapter() {

                public void windowClosing(WindowEvent e)

                {

                    System.exit(0);

                }

            });

 

            win.setBounds(200, 180,550,360);

            win.setVisible(true);

        }

     }

界面和測試類就完成了。

 四、實驗總結

在本節實驗中,我們完成了項目的創建以及 GUI 界面,下節實驗我們將完善本節的遺留問題,實現界面組件事件響應邏輯和Java文件的編輯、編譯及運行。主要包括兩個方法:

public void actionPerformed(ActionEvent e)

public void run()

 No2、實現功能

一、實驗介紹

1.1 實驗內容

在上節實驗中我們完成了編輯器的界面,可是按鈕的響應功能並未完成,在本節實驗中我們將實現界面組件事件響應邏輯和Java文件的編輯、編譯及運行。主要包括兩個方法:

 public void actionPerformed(ActionEvent e)

public void run()

1.2 實驗知識點

Java Swing 編程

IO 流操作

Runtime 類

Thread 的使用

1.3 實驗環境

本實驗環境采用帶桌面的 Ubuntu Linux 環境,實驗中會用到的環境或軟件:

 JDK1.7

Eclipse。

1.4 適合人群

本節課程難度較難,屬於中級課程,適合對 Java 核心 API 有較深入理解的同學學習。

 1.5 代碼獲取

你可以在Xfce終端下通過下面命令將實驗的完整工程項目下載到實驗樓環境中,作為參照對比進行學習。

 $ wget http://labfile.oss.aliyuncs.com/courses/287/MyEdit.tar.gz

二、項目文件結構

三、實驗步驟

 3.1 actionPerformed 方法的實現

首先咱們實現 public void actionPerformed(ActionEvent e) 這個方法。代碼中的注釋進行了詳細的講解。

     public void actionPerformed(ActionEvent e)

        {

            if(e.getSource()==button_input_txt)

            {    //顯示程序輸入區

                mycard.show(p,"input");

            }

            else if(e.getSource()==button_compiler_text)

            {    //顯示編譯結果顯示區

                mycard.show(p,"compiler");

            }

            else if(e.getSource()==button_see_doswin)

            {    //顯示程序運行結果區

                mycard.show(p,"dos");

            }

            else if(e.getSource()==button_compiler)

            {    //如果是編譯按鈕,執行編譯文件的方法

                if(!(compiler.isAlive()))

                {

                    compiler=new Thread(this);

                }

                try {

                    compiler.start();

 

                } catch (Exception e2) {

                    // TODO: handle exception

                    e2.printStackTrace();

                }

 

                mycard.show(p,"compiler");

 

            }

            else if(e.getSource()==button_run_prom)

            {    //如果是運行按鈕,執行運行文件的方法

                if(!(run_prom.isAlive()))

                {

                    run_prom=new Thread(this);

                }

                try {

                    run_prom.start();

                } catch (Exception e2) {

                    // TODO: handle exception

                    e2.printStackTrace();

                }

                mycard.show(p,"dos");

            }

         }

以上的代碼就是通過比較來判斷需要處理哪些事件。

 3.2 run 方法的實現

然后就剩一個 run() 方法,也是最重要的一個方法。在這個方法里會判斷是編譯還是運行:

 如果當前Thread是編譯,那么會將程序輸入區中的代碼以.java文件的形式保存到項目的當前目錄下,並通過javac命令執行剛才保存的.java文件生成.class文件,編譯后的信息會輸出到編譯結果顯示區。

 如果當前Thread是運行,那么會通過java命令執行編譯生成的.class文件,並將程序結果顯示到程序運行結果區中。

     public void run() {

             //TODO Auto-generated method stub

            if(Thread.currentThread()==compiler)

            {

            compiler_text.setText(null);

            String temp=input_text.getText().trim();

            byte [] buffer=temp.getBytes();

            int b=buffer.length;

            String file_name=null;

       file_name=input_file_name_text.getText().trim();

 

      try {

    file_saved=new File(file_name);

    FileOutputStream writefile=null;

    writefile=new FileOutputStream(file_saved);

    writefile.write(buffer, 0, b);

    writefile.close();

            } catch (Exception e) {

                    // TODO: handle exception

                    System.out.println("ERROR");

                }

    try {

     //獲得該進程的錯誤流,才可以知道運行結果到底是失敗了還是成功。

     Runtime rt=Runtime.getRuntime();

       InputStream in=rt.exec("javac "+file_name).getErrorStream(); //通過Runtime調用javac命令。注意:“javac ”這個字符串是有一個空格的!!

         BufferedInputStream bufIn=new BufferedInputStream(in);

         byte[] shuzu=new byte[100];

        int n=0;

        boolean flag=true;

         //輸入錯誤信息       

    while((n=bufIn.read(shuzu, 0,shuzu.length))!=-1)

        {

            String s=null;

              s=new String(shuzu,0,n);

            compiler_text.append(s);

            if(s!=null)

                        {

                            flag=false;

                        }

            }

                    //判斷是否編譯成功

                    if(flag)

                    {

                        compiler_text.append("Compile Succeed!");

                    }

                } catch (Exception e) {

                    // TODO: handle exception

                }

            }

    else if(Thread.currentThread()==run_prom)

        {

            //運行文件,並將結果輸出到dos_out_text

 

        dos_out_text.setText(null);

 

        try {

              Runtime rt=Runtime.getRuntime();

    String path=run_file_name_text.getText().trim();

    Process stream=rt.exec("java "+path);//調用java命令

 

    InputStream in=stream.getInputStream();

                    BufferedInputStream bisErr=new BufferedInputStream(stream.getErrorStream());

                    BufferedInputStream bisIn=new BufferedInputStream(in);

 

    byte[] buf=new byte[150];

    byte[] err_buf=new byte[150];

 

    @SuppressWarnings("unused")

    int m=0;

    @SuppressWarnings("unused")

    int i=0;

    String s=null;

    String err=null;

 

    //打印編譯信息及錯誤信息

    while((m=bisIn.read(buf, 0, 150))!=-1)

                    {

                        s=new String(buf,0,150);

                        dos_out_text.append(s);

                    }

                                  while((i=bisErr.read(err_buf))!=-1)

                    {

                    err=new String(err_buf,0,150);

                    dos_out_text.append(err);

                    }

        }

         catch (Exception e) {

                    // TODO: handle exception

                    }

        }

     }

 3.3 進行簡單測試

點擊編譯按鈕會出現錯誤信息,證明距離成功不遠了。

 運行按鈕錯誤:

 點擊按鈕在程序輸入區(白色),寫一個簡單的測試小程序吧!代碼如下:

     class a

    {

        public static void main(String [] args)

        {

            System.out.println("Hello ShiYanLou");

        }

    }

 接着在輸入編譯文件名(.java)后面的文本框里填入與類名相同的.java文件,如a.java,點擊編譯程序。

如果程序沒有出錯,那么編譯結果顯示如下:

 在輸入應用程序主類名后面的文本框里填入類名,如a,點擊運行程序。

 程序的運行結果將會顯示在淺藍色的文本域中。

 重新編輯的時候需要點擊按鈕在程序輸入區(白色),在白色文本域進行輸入。

三、實驗總結

至此,我們終於完成了整個程序,實現了編輯Java代碼、編譯和運行Java文件的功能。本次課程涉及的知識點比較復雜,特別是 Runtime 類和 Thread 的使用,希望同學們下來能夠對這些知識點進行鞏固。

五、課后習題

同學們下來考慮如何豐富其功能,例如 “代碼高亮”、“代碼自動補全” 等等。這些功能有的比較難,不一定要實現,但要勤於思考。

具體代碼:

FileWindow.java

package com.hijackykun.myedit;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;

@SuppressWarnings("serial")
//壓制信息,不會的同學可以不理會。
public class FileWindow extends JFrame implements ActionListener,Runnable{
     /*注意:因為實現了ActionListener 和Runnable接口,所以必須要實現這兩個接口的方法。
      * 這里我們先把這兩個方法簡單實現以下。下節課將徹底完成這兩個方法。*/
    Thread compiler = null;
    Thread run_prom = null;
    boolean bn = true;
    CardLayout mycard;  //聲明布局,以后會用到
    File file_saved = null;
    JButton button_input_txt,   //按鈕的定義
            button_compiler_text,
            button_compiler,
            button_run_prom,
            button_see_doswin;
    JPanel p = new JPanel();
    JTextArea input_text = new JTextArea(); // 程序輸入區
    JTextArea compiler_text = new JTextArea();// 編譯錯誤顯示區
    JTextArea dos_out_text = new JTextArea();// 程序的輸出信息
    JTextField input_file_name_text = new JTextField();
    JTextField run_file_name_text = new JTextField();
    public FileWindow() {
        // TODO Auto-generated constructor stub
        super("Java語言編譯器");
        mycard = new CardLayout();
        compiler=new Thread(this);
        run_prom=new Thread(this);
        button_input_txt=new JButton("程序輸入區(白色)");
        button_compiler_text=new JButton("編譯結果區(粉紅色)");
        button_see_doswin=new JButton("程序運行結果(淺藍色)");
        button_compiler=new JButton("編譯程序");
        button_run_prom=new JButton("運行程序");
        p.setLayout(mycard);//設置卡片布局
        p.add("input",input_text);//定義卡片名稱
        p.add("compiler", compiler_text);
        p.add("dos",dos_out_text);
        add(p,"Center");

        compiler_text.setBackground(Color.pink); //設置顏色
        dos_out_text.setBackground(Color.cyan);
        JPanel p1=new JPanel();

        p1.setLayout(new GridLayout(3, 3)); //設置表格布局
        //添加組件
        p1.add(button_input_txt);
        p1.add(button_compiler_text);
        p1.add(button_see_doswin);
        p1.add(new JLabel("輸入編譯文件名(.java):"));
        p1.add(input_file_name_text);
        p1.add(button_compiler);
        p1.add(new JLabel("輸入應用程序主類名"));
        p1.add(run_file_name_text);
        p1.add(button_run_prom);
        add(p1,"North");

        //定義事件
        button_input_txt.addActionListener(this);
        button_compiler.addActionListener(this);
        button_compiler_text.addActionListener(this);
        button_run_prom.addActionListener(this);
        button_see_doswin.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // TODO Auto-generated method stub
         if(e.getSource()==button_input_txt)
         {    //顯示程序輸入區
             mycard.show(p,"input");
         }
         else if(e.getSource()==button_compiler_text)
         {    //顯示編譯結果顯示區
             mycard.show(p,"compiler");
         }
         else if(e.getSource()==button_see_doswin)
         {    //顯示程序運行結果區
             mycard.show(p,"dos");
         }
         else if(e.getSource()==button_compiler)
         {    //如果是編譯按鈕,執行編譯文件的方法
             if(!(compiler.isAlive()))
             {
                 compiler=new Thread(this);
             }
             try {
                 compiler.start();

             } catch (Exception e2) {
                 // TODO: handle exception
                 e2.printStackTrace();
             }

             mycard.show(p,"compiler");

         }
         else if(e.getSource()==button_run_prom)
         {    //如果是運行按鈕,執行運行文件的方法
             if(!(run_prom.isAlive()))
             {
                 run_prom=new Thread(this);
             }
             try {
                 run_prom.start();
             } catch (Exception e2) {
                 // TODO: handle exception
                 e2.printStackTrace();
             }
             mycard.show(p,"dos");
         }

    }
    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        if (Thread.currentThread() == compiler) {
            compiler_text.setText(null);
            String temp = input_text.getText().trim();
            byte[] buffer = temp.getBytes();
            int b = buffer.length;
            String file_name = null;
            file_name = input_file_name_text.getText().trim();

            try {
                file_saved = new File(file_name);
                FileOutputStream writefile = null;
                writefile = new FileOutputStream(file_saved);
                writefile.write(buffer, 0, b);
                writefile.close();
            } catch (Exception e) {
                // TODO: handle exception
                System.out.println("ERROR");
            }
            try {
                // 獲得該進程的錯誤流,才可以知道運行結果到底是失敗了還是成功。
                Runtime rt = Runtime.getRuntime();
                InputStream in = rt.exec("javac " + file_name).getErrorStream(); 
                // 通過Runtime調用javac命令。注意:“javac ”這個字符串是有一個空格的!!
                BufferedInputStream bufIn = new BufferedInputStream(in);
                byte[] shuzu = new byte[100];
                int n = 0;
                boolean flag = true;
                // 輸入錯誤信息
                while ((n = bufIn.read(shuzu, 0, shuzu.length)) != -1) {
                    String s = null;
                    s = new String(shuzu, 0, n);
                    compiler_text.append(s);
                    if (s != null) {
                        flag = false;
                    }
                }
                // 判斷是否編譯成功
                if (flag) {
                    compiler_text.append("Compile Succeed!");
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        } else if (Thread.currentThread() == run_prom) {
            // 運行文件,並將結果輸出到dos_out_text
            dos_out_text.setText(null);
            try {
                Runtime rt = Runtime.getRuntime();
                String path = run_file_name_text.getText().trim();
                Process stream = rt.exec("java " + path);// 調用java命令
                InputStream in = stream.getInputStream();
                BufferedInputStream bisErr = new BufferedInputStream(
                        stream.getErrorStream());
                BufferedInputStream bisIn = new BufferedInputStream(in);
                byte[] buf = new byte[150];
                byte[] err_buf = new byte[150];
                @SuppressWarnings("unused")
                int m = 0;
                @SuppressWarnings("unused")
                int i = 0;
                String s = null;
                String err = null;
                // 打印編譯信息及錯誤信息
                while ((m = bisIn.read(buf, 0, 150)) != -1) {
                    s = new String(buf, 0, 150);
                    dos_out_text.append(s);
                }
                while ((i = bisErr.read(err_buf)) != -1) {
                    err = new String(err_buf, 0, 150);
                    dos_out_text.append(err);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    }
}
View Code

Main.java

package com.hijackykun.myedit;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Main {

    public static void main(String[] args) {
        FileWindow win =new FileWindow();
        win.pack();//根據窗口里面的布局及組件的preferredSize來確定frame的最佳大小
        win.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        //setBounds(x,y,width,height); 
        //x:組件在容器X軸上的起點 y:組件在容器Y軸上的起點 width:組件的長度 height:組件的高度
        win.setBounds(200, 180, 550, 360);
        win.setVisible(true);
    }

}
View Code

參考來源:https://www.shiyanlou.com/courses/287


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM