• 02.制做一个本身的 Java 编辑器

    2022-01-13
  • 这篇文章主要向大家介绍02.制做一个本身的 Java 编辑器,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

    难度中等,适合 Java 基础扎实,对 Java 核心 API 有所熟悉的同窗学习java

    No一、制做GUI界面编程

    1、实验介绍小程序

    1.1 实验内容app

    本节课程的主要内容是准备开发环境,创建项目并完成 GUI 界面的编程实现。编辑器

    1.2 实验知识点ide

    Java Swing 编程布局

    1.3 实验环境学习

    本实验环境采用带桌面的 Ubuntu Linux 环境,实验中会用到的环境或软件:测试

    JDK1.7this

    Eclipse。

    1.4 适合人群

    本节课程难度较低,属于初级课程,适合要想学习Java Swing 编程的同窗学习。

    1.5 代码获取

    你能够在Xfce终端下经过下面命令将实验的完整工程项目下载到实验楼环境中,做为参照对比进行学习。

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

    2、项目文件结构 

    3、实验步骤

    这一节咱们将开发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);

      }

        }

    界面和测试类就完成了。

     4、实验总结

    在本节实验中,咱们完成了项目的建立以及 GUI 界面,下节实验咱们将完善本节的遗留问题,实现界面组件事件响应逻辑和Java文件的编辑、编译及运行。主要包括两个方法:

    public void actionPerformed(ActionEvent e)

    public void run()

     No二、实现功能

    1、实验介绍

    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

    2、项目文件结构

    3、实验步骤

     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,点击运行程序。

     程序的运行结果将会显示在浅蓝色的文本域中。

     从新编辑的时候须要点击按钮在程序输入区(白色),在白色文本域进行输入。

    3、实验总结

    至此,咱们终于完成了整个程序,实现了编辑Java代码、编译和运行Java文件的功能。本次课程涉及的知识点比较复杂,特别是 Runtime 类和 Thread 的使用,但愿同窗们下来可以对这些知识点进行巩固。

    5、课后习题

    同窗们下来考虑如何丰富其功能,例如 “代码高亮”、“代码自动补全” 等等。这些功能有的比较难,不必定要实现,但要勤于思考。

    具体代码:

    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