动机:今天看同学在牛客做题,看到提交了主程序很快就可以编译并得出结果,但是,在提交的主函数中并没有写任何的输入输出函数,因此考虑他们肯定使用了自己的类加载器,然后在类加载器中修改了标准输入输出流,使其指向测试文件,本博文探索了这一点,当然还有一些部分没有做,比如权限限制、用例检验等,有感兴趣的同学可以一起交流一下。
用例数据,将下面的数据,放在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
- 需要加载的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();
}
}
}
本测试程序来自我一个同学,感谢其友情赞助。
- 类加载器
下面这个类是一个类加载程序,继承自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;
}
}
- 主测试程序,调用自定义类加载器,将指定类加载进来,并运行,这里运行的是其主函数。
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();
}
}
}
}