【Java项目】 在线OJ系统

项目Gitee源码链接:https://gitee.com/zx201014/ojproject
项目GitHub源码链接:https://github.com/zx04180521/OJProject

项目简介

在线OJ系统就是大家平时刷题的网站,比如力扣,我的这个系统就是模仿力扣网站做了一个简单的OJ系统,不过目前只实现了部分功能,比如展示题目列表,查看题目详情,编写代码,提交运行

1.项目实现步骤

1.1数据库的创建

这个项目的数据库比较简单,只有一张表:题目表,其中表的字段有题目id、题目名称、题目难度、题目描述、题目模板代码、题目测试代码。

解释:题目的模板代码指的是用户可以看到的代码,也就是给用户提供了一个实现的函数,题目的测试代码指的是用户提交代码之后,进行运行校验结果时的测试代码,也就是主函数

创建数据库代码:

create database if not exists ojProject;

use ojProject;
create table oj_table(
    id int primary key auto_increment,
    title varchar(50),
    level varchar(20),
    description varchar(4096),
    templateCode varchar(4096),
    testCode varchar(4096)
);

1.2创建数据库工具类Util

在这个类中,能够向其他类提供建立数据库连接的接口以及关闭数据库连接的接口

public class DBUtil {
    //连接数据库的URL
    public static String URL = "jdbc:mysql://localhost:3306/ojProject?characterEncoding=utf8&useSSL=true";
    //数据库的用户名
    public static String USER = "root";
    //数据库的密码
    public static String PASSWORD = "123456";
    //数据库连接池对象
    public static volatile MysqlDataSource dataSource = null;
    //创建数据库连接对象的方法
    // 在这个方法中,使用的双重判断加上synchronize锁的单例模式
    // 能够保证在多线程下只创建出一个数据库连接池对象
    public static MysqlDataSource getDataSource() {
        if (dataSource == null) {
            synchronized (DBUtil.class) {
                if (dataSource == null) {
                    dataSource = new MysqlDataSource();
                    dataSource.setURL(URL);
                    dataSource.setUser(USER);
                    dataSource.setPassword(PASSWORD);
                }
            }
        }
        return dataSource;
    }

    //获取数据库连接的方法
    public static Connection getConnection() {
        try {
            return getDataSource().getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    //关闭数据库连接的方法
    //其中关闭数据库连接必须按照ResultSet、PrepareStatement、Connection的顺序关闭
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

1.3创建能够实现运行命令的类CommandUtil

在这个类中,对外提供了一个run(String cmd, String stdoutFile, String stderrFile)方法。
其中第一个参数cmd表示需要执行的指令,比如javac、java等等,运行这些指令之后会有标准错误以及标准输出的内容,比如运行java指令之后的标准输出指的是程序运行的结果,对应的标准错误指的是程序运行出错的原因。
第二个参数表示运行指令之后将运行的标准输出存储到参数指定的文件中
第三个参数表示运行指令之后将运行的标准错误存储到参数指定的文件中

public class CommandUtil {
    //run方法主要用于进行创建线程并执行命令
    //cmd表示要执行的命令,比如javac
    //stdoutFile指定标准输出写到哪个文件
    //stderrFile指定标准错误写到那个文件中
    public static int run(String cmd, String stdoutFile,
                          String stderrFile) throws IOException, InterruptedException {
        //java中对于多进程的操作比较粗糙,不方便进行精细化的操作
        //但是当前业够用
        //使用Runtime这样的类来创建进程
        //注意Runtime比较特殊,使用的时候不需要手动创建实例,而是使用现有的实际就可以了
        //一个java程序中,Runtime的实例只有一个,不应该有多个,成为"单例模式"
        //Process对象其实就是用来表示新创建出来的进程
        //新进程创建出来之后,新的进程和当前的进程是一个并列关系,并发执行,
        // 但是执行的先后顺序不确定
        Process process = Runtime.getRuntime().exec(cmd);
        //当新的进程跑起来时候。就需要获取新的进程的输出结果
        if (stdoutFile != null) {
            //读入写出
            //getInputStream()得到的标准输出
            // 通过这个对象,就可以读取到新进程的标准输出内容
            InputStream stdoutFrom = process.getInputStream();

            FileOutputStream stdoutTo = new FileOutputStream(stdoutFile);
            //接下来就从新进程一次读取每个字节,写入到stdoutFile文件中
            while (true) {
                int c = stdoutFrom.read();
                if (c == -1) {
                    break;
                }
                stdoutTo.write(c);
            }
            //文件读写完毕,关闭文件
            stdoutFrom.close();
            stdoutTo.close();
        }
        if (stderrFile != null) {
            //getErrorStream()得到的是标准错误
            InputStream stderrFrom = process.getErrorStream();
            FileOutputStream stderrTo = new FileOutputStream(stderrFile);
            while (true) {
                int c = stderrFrom.read();
                if (c == -1) {
                    break;
                }
                stderrTo.write(c);
            }
            stderrFrom.close();
            stderrTo.close();
        }
        //等待新进程结束并获取到退出码
        int exitCode = process.waitFor();
        return exitCode;
    }
}

1.4创建生成目录以及构造指令的类Task

在这个类中,我们需要在构造方法中每次生成独立目录,目的就是为了多次执行指令两两之间不会互相影响
生成随机目录我用的方法是UUID.randomUUID()
1.这个类涉及到最终的编译运行,左移方法参数中需要传进来最终需要编译运行的代码,这个代码通过自定义的类Quesition来表示,里面只要一个字符串属性 code 表示最终的代码,还有对应的get,set方法
2. 将最终的响应结果也包装成一个Answer类,属性有整型的错误码error,0表示运行成功,1表示编译出错,2表示运行出错,还有属性reason 表示错误的原因,stdout标准输出的内容。

Quesition类的代码:

//表示编译运行的输入
public class Question {
    //编译运行的代码
    //用户在网页上编辑的代码
    private String code;

    public void setCode(String code) {
        this.code = code;
    }

    public String getCode() {
        return code;
    }
}

Answer类:

package compile;

//表示编译运行的结果
public class Answer {
    //这里的字段就是最终反馈到页面的信息
    //运行结果是否正确
    // 0表示编译运行正常,1 表示编译运行出错,2 表示运行异常
    private int error;
    //如果出错了,原因是什么
    // error 是 1,reason包含了编译错误的信息
    // error 是 2,reason包含了异常的调用栈信息
    private String reason;
    //程序的标准输出
    private String stdout;
    //程序的标准错误
//    private String stderr;
    public int getError() {
        return error;
    }

    public void setError(int error) {
        this.error = error;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }

    public String getStdout() {
        return stdout;
    }

    public void setStdout(String stdout) {
        this.stdout = stdout;
    }


    @Override
    public String toString() {
        return "Answer{" +
                "error=" + error +
                ", reason='" + reason + '\'' +
                ", stdout='" + stdout + '\'' +
                '}';
    }
}

Tast类

package compile;

import Util.FileUtil;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

//用这个类表示一个完整的编译运行过程
public class Task {
    //此处罗列出需要的临时文件(用于进程间通信) 文件名约定
    //所有的临时文件要放到这个目录中
    private String WORK_DIR;
    //要编译执行的类的名字,影响到源代码的文件名
    private String CLASS = "Solution";
    //要编译执行的文件名
    private String CODE;
    //程序标准输出放置的文件
    private String STDOUT;
    //程序标准错误放置的文件
    private String STDERR;
    //程序编译出错详细信息放置的文件
    private String COMPILE_ERROR;

    public Task(){
        //生成唯一ID,根据这个id拼装出目录的名字
        WORK_DIR="./tmp/"+ UUID.randomUUID().toString()+"/";
        CODE=WORK_DIR+CLASS+".java";
        STDOUT=WORK_DIR+"stdout.txt";
        STDERR=WORK_DIR+"stderr.txt";
        COMPILE_ERROR=WORK_DIR+"compile_error.txt";
    }

    //Question表示用户提交的代码
    //Answer表示代码的编译运行结果
    public Answer compileAndRun(Question question) throws IOException, InterruptedException {
        Answer answer=new Answer();
        File file=new File(WORK_DIR);
        if(!file.exists()){
            //创建对应的目录
            file.mkdirs();
        }
        //1.先准备需要用到的临时文件
        //  要编译的原代码的文件
        //  编译出错要放到一个文件中
        //  最终的运行标准输出 标准错误分别放到文件中
        FileUtil.writeFile(CODE,question.getCode());
        //2.构造编译指令(javac),并进行执行,预期得到的结果
        //  就是一个.class文件,以及编译出错文件
        //-d 表示 生成的 .class 文件的放置位置
        // javac -encoding utf-8 ./tmp/UUID/Solution.java -d ./tmp/UUID/
        String compileCmd=String.format("javac -encoding utf-8 %s -d %s",CODE,WORK_DIR);
        System.out.println(compileCmd);
        CommandUtil.run(compileCmd,null,COMPILE_ERROR);
        //此处需要判断一下编译是否出错,判断COMPILE_ERROR是否为空即可
        String compileError=FileUtil.readFile(COMPILE_ERROR);
        if(!compileError.equals("")){
            //得到的compileError不为空,说明编译出错
            answer.setError(1);
            answer.setReason(compileError);
            return answer;
        }
        //3.构造运行指令(java),并进行执行,预期得到的结果
        //  就是这个代码标准输出文件 和 标准错误文件
        String runCmd=String.format("java -classpath %s %s",WORK_DIR,CLASS);
        System.out.println(runCmd);
        CommandUtil.run(runCmd,STDOUT,STDERR);
        String runErr=FileUtil.readFile(STDERR);
        if(!runErr.equals("")){
            answer.setError(2);
            answer.setReason(runErr);
            return answer;
        }
        //4.把最终结果构造成 Answer 对象,并返回
        String stdOut=FileUtil.readFile(STDOUT);
        answer.setError(0);
        answer.setStdout(stdOut);
        return answer;
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        Task task=new Task();
        Question question=new Question();
        question.setCode("public class Solution {\n" +
                "    public static void main(String[] args) {\n" +
                "        System.out.println(\"hello\");\n" +
                "    }\n" +
                "}");
        System.out.println(task.compileAndRun(question));
    }
}

1.5创建数据库的实体类Problem以及数据库操作类ProblemDAO

实体类Problem就是数据库表对应的类,参数就是表中的属性
数据库操作类ProblemDAO就是JDBC代码,主要有插入操作,删除操作,查询一个题目详情操作,操作所有题目列表操作

ProblemDAO代码

public class ProblemDAO {
    //向数据库中插入一条数据
    public void insert(Problem problem){
        //获取连接
        Connection connection= DBUtil.getConnection();
        PreparedStatement statement=null;
        try {
            //拼装SQL语句
            String sql="insert into oj_table values(null,?,?,?,?,?)";
            statement=connection.prepareStatement(sql);
            statement.setString(1,problem.getTitle());
            statement.setString(2,problem.getLevel());
            statement.setString(3,problem.getDescription());
            statement.setString(4,problem.getTemplateCode());
            statement.setString(5,problem.getTestCode());
            //执行SQL
            statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放连接
            DBUtil.close(connection,statement,null);
        }
    }

    public boolean delete(int problemId){
        //建立连接
        Connection connection=DBUtil.getConnection();
        PreparedStatement statement=null;
        //拼装SQL
        String sql="delete from  oj_table where id=?";
        int res=0;
        try {
            statement=connection.prepareStatement(sql);
            statement.setInt(1,problemId);
            //执行SQL
            res=statement.executeUpdate();

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放连接
            DBUtil.close(connection,statement,null);
            return res>0;
        }
    }

    //查找全部题目(用来实现题目列表功能)
    //只需要查找 id  title  level
    public List<Problem> selectAll(){
        List<Problem> problems=new ArrayList<>();
        //建立连接
        Connection connection=DBUtil.getConnection();
        PreparedStatement statement=null;
        //拼装SQL
        String sql="select id,title,level from oj_table";
        try {
            statement=connection.prepareStatement(sql);
            //执行SQL
            ResultSet resultSet=statement.executeQuery();
            while(resultSet.next()){
                Problem problem=new Problem();
                problem.setId(resultSet.getInt("id"));
                problem.setTitle(resultSet.getString("title"));
                problem.setLevel(resultSet.getString("level"));
                problems.add(problem);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            DBUtil.close(connection,statement,null);
        }
        return problems;
    }

    //查找指定题目(实现题目详情页面功能)
    //需要Problem的每个字段
    public Problem selectOne(int problemId){
        //建立连接
        Connection connection=DBUtil.getConnection();
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        //构造SQL
        String sql="select * from oj_table where id=?";
        try {
            statement=connection.prepareStatement(sql);
            statement.setInt(1,problemId);
            resultSet=statement.executeQuery();

            if (resultSet.next()) {
                Problem problem=new Problem();
                problem.setId(resultSet.getInt("id"));
                problem.setTitle(resultSet.getString("title"));
                problem.setLevel(resultSet.getString("level"));
                problem.setDescription(resultSet.getString("description"));
                problem.setTemplateCode(resultSet.getString("templateCode"));
                problem.setTestCode(resultSet.getString("testCode"));
                return problem;
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            DBUtil.close(connection,statement,resultSet);
        }
        return null;
    }

Problem实体类代码


package problem;

public class Problem {
    //题目的编号
    private int id;
    //题目的标题
    private  String title;
    //题目的难度
    private String level;
    //题目的详细信息
    private String description;
    //题目的代码模板
    private String templateCode;

    @Override
    public String toString() {
        return "Problem{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", level='" + level + '\'' +
                ", description='" + description + '\'' +
                ", templateCode='" + templateCode + '\'' +
                ", testCode='" + testCode + '\'' +
                '}';
    }

    //题目的测试用例代码
    private String testCode;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getLevel() {
        return level;
    }

    public void setLevel(String level) {
        this.level = level;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getTemplateCode() {
        return templateCode;
    }

    public void setTemplateCode(String templateCode) {
        this.templateCode = templateCode;
    }

    public String getTestCode() {
        return testCode;
    }

    public void setTestCode(String testCode) {
        this.testCode = testCode;
    }
}

1.6 创建Servlet代码

  1. 请求的实体类 CompileRequest,主要就是将从前端请求的数据包装成一个类对象,属性有题目的id,请求的代码code
  2. 响应的实体类CompileResponse,这个和刚才的Answer一样的功能,主要就是为了区分
  3. CompileServlet,主要功能就是接受前端数据并解析,第一步就是解析前端数据的body并创建成CompileRequest对象,第二步根据题目的id得到题目的测试代码,第三步将测试代码和前端请求的代码进行字符串拼接,形成完整的代码并构造成Question对象,第四步创建Task类,调用run()方法,将构造的Question作为参数传入,run的返回值为Answer对象,在通过这个对象构造CompileResponse对象,序列化为json格式字符串返回给前端页面
  4. ProblemServlet数据库操作的Servlet类
    CompileRequest
package api;

public class CompileRequest {
    private int id;
    private String code;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

CompileResponse

package api;

public class CompileResponse {
    private int error;
    private String reason;
    private String stdout;

    public int getError() {
        return error;
    }

    public void setError(int error) {
        this.error = error;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }

    public String getStdout() {
        return stdout;
    }

    public void setStdout(String stdout) {
        this.stdout = stdout;
    }
}

CompileServlet

package api;

import Util.HttpUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import compile.Answer;
import compile.Question;
import compile.Task;
import problem.Problem;
import problem.ProblemDAO;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/compile")
public class CompileServlet extends HttpServlet {
    private Gson gson=new GsonBuilder().create();
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.先从请求中读取body信息
        String body= HttpUtil.readBody(req);
        //2.把这个json数据转换成请求对象
        CompileRequest compileRequest=gson.fromJson(body,CompileRequest.class);
        //3.根据数据中的id获取数据库中测试用例代码
        ProblemDAO problemDAO=new ProblemDAO();
        Problem problem=problemDAO.selectOne(compileRequest.getId());
        //testCode 就是当前这个题目的测试代码b
        String testCode=problem.getTestCode();
        //4.把这个用户提交的代码和测试代码拼接在一起。拼接成一个完整的代码
        //  requestCode就表示用户提交的代码
        //  finalCode就表示测试代码
        String requestCode=compileRequest.getCode();
        String finalCode=merge(requestCode,testCode);
        //5.创建task对象,借助Task对象完成编译运行这个代码
        Task task=new Task();
        Question question=new Question();
        question.setCode(finalCode);
        Answer answer=null;
        try {
            answer=task.compileAndRun(question);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //把这个运行结果包装成相应数据,吧数据返回给页面即可
        CompileResponse compileResponse=new CompileResponse();
        compileResponse.setError(answer.getError());
        compileResponse.setReason(answer.getReason());
        compileResponse.setStdout(answer.getStdout());
        String respString=gson.toJson(compileResponse);
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().write(respString);
    }
    private String merge(String requestCode,String testCode){
        //先找到requestCode末尾的 } ,并截取出前面的代码
        int pos=requestCode.lastIndexOf("}");
        if(pos==-1){
            return null;
        }
        //2.把testCode拼接到后面再拼接上 } 即可
        return requestCode.substring(0,pos)+testCode+"}";
    }
}

ProblemServlet

package api;

import Util.HttpUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import problem.Problem;
import problem.ProblemDAO;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/problem")
public class ProblemServlet extends HttpServlet {
    private Gson gson=new GsonBuilder().create();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //查询题目列表/详细信息
        req.setCharacterEncoding("utf-8");
        resp.setContentType("application/json;charset=utf-8");
        String id=req.getParameter("id");
        if(id==null||id.equals("")){
            //查找题目列表
            selectAll(resp);
        }else{
            //查看题目详情页面
            selectOne(Integer.parseInt(id),resp);
        }
    }

    private void selectOne(int parseInt, HttpServletResponse resp) throws IOException {
        //创建ProblemDAO对象
        ProblemDAO problemDAO=new ProblemDAO();
        //查找指定的结果
        Problem problem=problemDAO.selectOne(parseInt);
        //将结果包装成JSOn格式
        String respString=gson.toJson(problem);
        //将结果写回前端
        resp.getWriter().write(respString);
    }

    private void selectAll(HttpServletResponse resp) throws IOException {
        //创建ProblemDAO对象
        ProblemDAO problemDAO=new ProblemDAO();
        //查找所有结果
        List<Problem> problems=problemDAO.selectAll();
        //将结果包装成JSON格式
        String respString=gson.toJson(problems);
        //把结果写会前端
        resp.getWriter().write(respString);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //插入一个题目
        //1.读取到请求中的body信息
        //  进一步就可以插入数据库
        String body= HttpUtil.readBody(req);
        Problem problem=gson.fromJson(body,Problem.class);
        ProblemDAO problemDAO=new ProblemDAO();
        problemDAO.insert(problem);
        req.setCharacterEncoding("utf-8");
        resp.setContentType("application/json;charset=utf-8");
        resp.getWriter().write("{\"ok\":1}");
    }

    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("application/json;charset=utf-8");
        String id=req.getParameter("id");
        if(id==null||id.equals("")){
            resp.getWriter().write("{\"ok\":0,\"reason\":\"id不存在\"}");
            return;
        }
        ProblemDAO problemDAO=new ProblemDAO();
        problemDAO.delete(Integer.parseInt(id));
        resp.getWriter().write("{\"ok\":1}");
    }
}

1.7代码中用到的一些文件操作工具类

文件读写操作:FileUtil

package Util;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileUtil {
    public static void writeFile(String filePath,String content){
        FileOutputStream fileOutputStream=null;
        try {
             fileOutputStream=new FileOutputStream(filePath);
             fileOutputStream.write(content.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public static String readFile(String filePath){
        StringBuffer stringBuffer=new StringBuffer();
        FileInputStream fileInputStream=null;
        try {
             fileInputStream=new FileInputStream(filePath);
             while(true){
                 int c=fileInputStream.read();
                 if(c==-1){
                     break;
                 }
                 //每次read方法只能读取到一个字节
                 //read设计成返回int的原因知识为了能多表示 -1 的情况
                 //把结果往StringBuffer里面插的时候要转成char
                 stringBuffer.append((char)c);
             }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return stringBuffer.toString();
    }
}

读取请求body数据操作HttpUtil

package Util;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

public class HttpUtil {



    public static String readBody(HttpServletRequest req) throws UnsupportedEncodingException {
        //1.先获取请求中的body长度,进行分配空间
        //  单位是字节
        int contentLength=req.getContentLength();
        byte[] buf=new byte[contentLength];
        ServletInputStream inputStream=null;
        try {
            inputStream =req.getInputStream();
            inputStream.read(buf);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return new String(buf,"utf-8");
    }
}

2 单元测试

2.1插入数据测试

插入题目无重复字符的最长子串
代码:

    @Test
    public void insert() {
        Problem problem = new Problem();
        problem.setTitle("无重复字符的最长子串");
        problem.setLevel("中等");
        problem.setDescription("给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。\n" +
                "\n" +
                " \n" +
                "\n" +
                "示例 1:\n" +
                "\n" +
                "输入: s = \"abcabcbb\"\n" +
                "输出: 3 \n" +
                "解释: 因为无重复字符的最长子串是 \"abc\",所以其长度为 3。\n" +
                "示例 2:\n" +
                "\n" +
                "输入: s = \"bbbbb\"\n" +
                "输出: 1\n" +
                "解释: 因为无重复字符的最长子串是 \"b\",所以其长度为 1。\n" +
                "示例 3:\n" +
                "\n" +
                "输入: s = \"pwwkew\"\n" +
                "输出: 3\n" +
                "解释: 因为无重复字符的最长子串是 \"wke\",所以其长度为 3。\n" +
                "     请注意,你的答案必须是 子串 的长度,\"pwke\" 是一个子序列,不是子串。\n" +
                "示例 4:\n" +
                "\n" +
                "输入: s = \"\"\n" +
                "输出: 0\n" +
                " \n" +
                "\n" +
                "提示:\n" +
                "\n" +
                "0 <= s.length <= 5 * 104\n" +
                "s 由英文字母、数字、符号和空格组成");
        problem.setTemplateCode("class Solution {\n" +
                "    public int lengthOfLongestSubstring(String s) {\n" +
                "\n" +
                "    }\n" +
                "}");
        problem.setTestCode("    public static void main(String[] args) {\n" +
                "        Solution solution = new Solution();\n" +
                "        String str=\"pwwkew\";\n" +
                "        int result = solution.lengthOfLongestSubstring(str);\n" +
                "        if (result==3) {\n" +
                "            System.out.println(\"TestCase OK!\");\n" +
                "        } else {\n" +
                "            System.out.println(\"TestCase Failed! String=\\\"pwwkew\\\"\");\n" +
                "        }\n" +
                "\n" +
                "        String str2=\"abcabcbb\";\n" +
                "        int result2 = solution.lengthOfLongestSubstring(str);\n" +
                "        if (result2==3) {\n" +
                "            System.out.println(\"TestCaseOK!\");\n" +
                "        } else {\n" +
                "            System.out.println(\"TestCase Failed! String=\\\"abcabcbb\\\"\");\n" +
                "        }\n" +
                "    }");
        ProblemDAO problemDAO = new ProblemDAO();
        problemDAO.insert(problem);
    }

数据库表
在这里插入图片描述
插入题目也有可能超出字段长度的范围

2.2删除数据测试

删除题目无重复字符的最长子串
代码

    @Test
    public void delete() {
        ProblemDAO problemDAO = new ProblemDAO();
        boolean res=problemDAO.delete(10);
        System.out.println(res);
    }

运行结果 true
数据库表:
在这里插入图片描述

2.3 查询所有题目列表

    @Test
    public void selectAll() {
        ProblemDAO problemDAO = new ProblemDAO();
        List<Problem> problems = problemDAO.selectAll();
        System.out.println(problems);
    }

运行结果
在这里插入图片描述
因为查询所有题目我们只需要题目id,题目标题,题目难度三个字段,所以其他字段为空

2.4 查询题目详情

    @Test
    public void selectOne() {
        ProblemDAO problemDAO = new ProblemDAO();
        Problem problem = problemDAO.selectOne(4);
        System.out.println(problem);
    }

在这里插入图片描述

2.5 Task编译运行的测试

   @Test
    public void compileAndRun() throws IOException, InterruptedException {
        Task task=new Task();
        Question question=new Question();
        question.setCode("public class Solution {\n" +
                "    public static void main(String[] args) {\n" +
                "        System.out.println(\"hello\");\n" +
                "    }\n" +
                "}");
        System.out.println(task.compileAndRun(question));
    }

运行结果
在这里插入图片描述
第一行的指令javac表示编译的指令,后面的参数是.java文件的目录以及文件名,第二个参数指生成的.class文件的存放位置
第二行的指令java表示运行指令,参数表示.class文件的位置以及类名
第三行表示运行之后的结果,error=0表示运行成功,标准输出是hello,也就是我的代码的运行结果

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值