java自定义类加载器模拟OJ调用

动机:今天看同学在牛客做题,看到提交了主程序很快就可以编译并得出结果,但是,在提交的主函数中并没有写任何的输入输出函数,因此考虑他们肯定使用了自己的类加载器,然后在类加载器中修改了标准输入输出流,使其指向测试文件,本博文探索了这一点,当然还有一些部分没有做,比如权限限制、用例检验等,有感兴趣的同学可以一起交流一下。

用例数据,将下面的数据,放在data.txt中,然后将data.txt放在项目根目录下。

3
2,5,6,7,9,5,7
1,7,4,3,4

4
2,5,6,7,9,5,7
1,7,4,3,4
  1. 需要加载的Main方法类。
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
 * 重排数组
 * Created by xxx on 2018/4/3 0003.
 * use java1.8
 */
 待加载类
public class Main {
    public static void main(String[] args) throws FileNotFoundException {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            int n = Integer.valueOf(scanner.nextLine());
            List<String> list = new ArrayList();
            while (true) {
                String a = scanner.nextLine();
                if (a == null || a.equals("")) {
                    break;
                }
                list.add(a);
            }
            StringBuilder sb = new StringBuilder();
            int i = 0;
            while (!list.isEmpty()) {
                String temp = list.get(i);
                String[] split = temp.split(",");
                int min = Math.min(n, split.length);
                for (int j = 0; j < min; j++) {
                    sb.append(split[j]);
                }
                StringBuilder sb2 = new StringBuilder();
                for (int j = min; j < split.length; j++) {
                    sb2.append(split[j] + ",");
                }
                if (sb2.toString().equals("")) {
                    list.remove(i);
                } else
                    list.set(i, sb2.toString());
                i++;
                if (i >= list.size()) {
                    i = 0;
                }
            }
            for (int j = 0; j < sb.length() - 1; j++) {
                System.out.print(sb.charAt(j) + ",");
            }
            if (sb.length() > 0) {
                System.out.print(sb.charAt(sb.length() - 1));
            }
            System.out.println();
        }
    }
}

本测试程序来自我一个同学,感谢其友情赞助。

  1. 类加载器
    下面这个类是一个类加载程序,继承自ClassLoader,实现了findClass,在findClass中调用了defineClass来进行类加载。
import java.io.*;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;

/**
 * 自定义类加载器
 * Created by FYZBXX on 2019/4/3 0003.
 */
public class MyClassLoader extends ClassLoader{
    private String classpath;
    private final AccessControlContext acc;

    public MyClassLoader(String classpath) {
        this.classpath = classpath;
        this.acc = AccessController.getContext();
    }

    /**
     * 查找并加载类。
     * @param className
     * @return 若加载成功就返回该类,否则抛出异常
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String className) throws ClassNotFoundException {
        byte[] classData = null;
        try {
        // 得到类字节流
            classData = getBytesOfClass(className);
        } catch (IOException e) {
            throw new ClassNotFoundException(className);
        }
        if(classData!=null){
            //defineClass方法将字节码转化为类
            return defineClass(className,classData,0,classData.length);
        }
        throw new ClassNotFoundException(className);
    }

    /**
     * 返回类的字节码
     * @param className className with classPath
     * @return
     * @throws IOException
     */
    private byte[] getBytesOfClass(String className) throws IOException{
        String path=classpath + File.separatorChar +
                className.replace('.',File.separatorChar)+".class";
        File file = new File(path);
        InputStream in = null;
        try {
            in=new FileInputStream(path);
            // 定义长度为4的整数倍
            int length = (((int) file.length())>>2)<<2;
            byte[] buffer=new byte[length];
            // 类的长度不可能太长,可以找个buffer一次性加载进来
            in.read(buffer);
            return buffer;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally{
            in.close();
        }
        return null;
    }
}
  1. 主测试程序,调用自定义类加载器,将指定类加载进来,并运行,这里运行的是其主函数。
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 测试类加载程序
 * Created by FYZBXX on 2019/4/3 0003.
 */
public class ClassLoaderTest {

	// 关闭可关闭句柄
    private static void close(Closeable... closeables) throws IOException {
        for (Closeable closeable:closeables
             ) {
            if(closeable!=null){
                closeable.close();
            }
        }
    }

    public static void main(String []args){
        //自定义类加载器的加载路径
//        System.out.println(new File(".").getAbsolutePath());
//        System.setSecurityManager(new SecurityManager());
        MyClassLoader myClassLoader=new MyClassLoader("target\\classes\\com\\company\\neu");
        Class c = null;
        try {
            //包名+类名
            c = myClassLoader.loadClass("com.company.neu.Main");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return;
        }
        if(c==null){
            System.out.println("加载失败,请检查路径!");
            return;
        }
        File ifile = new File("data.txt");
        File ofile = new File("result.txt");
        if (!ofile.exists()) {
            ofile.mkdirs();
        }
//        System.out.println(ifile.getAbsolutePath());
        FileInputStream fis = null;
        PrintStream printStream = null;
        try {
            fis = new FileInputStream(ifile);
            printStream = new PrintStream(ofile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            try {
                close(fis,printStream);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
        // 保存环境上下文,以便转换回来
        PrintStream out = System.out;
        InputStream in = System.in;
        // 重定义类标准输入输出流到指定文件
        System.setIn(fis);
        System.setOut(printStream);
        Method method= null;
        try {

            method = c.getMethod("main", String[].class);
            // 类静态方法,无需传入对象实例。
            method.invoke(null, (Object) new String[] {});
            //Object obj=c.newInstance();
            //传入对象实例也ok。传入参数需转换为Object类型,否则系统将当成多个参数。
            //method.invoke(obj, (Object) new String[] {})
//            System.out.println("执行完毕,重置标准输入输出。");
            // 记得及时换回来,养成良好习惯
            System.setIn(in);
            System.setOut(out);
            System.out.println("执行完毕!");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            try {
                close(fis,printStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值