Java笔记6:类加载

作者:陆金龙    发表时间:2022-09-23 22:05   

关键词:  

6.1 类的加载和初始化


class Tester
{
	static
	{
		System.out.println("Tester类的静态初始化块...");
	}
}

public class ClassLoaderTest{
	public static void main(String[] args)
		throws ClassNotFoundException{
		ClassLoader cl = ClassLoader.getSystemClassLoader();
		// 下面语句仅仅是加载Tester类
		cl.loadClass("Tester");
		System.out.println("系统加载Tester类");
		
		// 下面语句才会初始化Tester类,执行静态初始化块
		Class.forName("Tester");
	}
}
6.2 类加载器
 
6.2.1 根类加载器(加载核心类库):
BootStrap
// 获取根类加载器所加载的全部URL数组
URL[] urls = sun.misc.Launcher.
getBootstrapClassPath().getURLs();
// 遍历、输出根类加载器加载的全部URL
for (int i = 0; i < urls.length; i++)
{
System.out.println(urls[i].toExternalForm());
}
 
6.2.2 系统类加载器(JVM启动时):程序运行的当前路径
// 获取系统类加载器
ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器:" + systemLoader);
/*
获取系统类加载器的加载路径——通常由CLASSPATH环境变量指定
如果操作系统没有指定CLASSPATH环境变量,默认以当前路径作为
系统类加载器的加载路径
*/
Enumeration<URL> em1 = systemLoader.getResources("");
while(em1.hasMoreElements())
{
System.out.println(em1.nextElement());
}
 
6.2.3 扩展类加载器:路径是
// 获取系统类加载器的父类加载器:得到扩展类加载器
ClassLoader extensionLader = systemLoader.getParent();
System.out.println("扩展类加载器:" + extensionLader);
System.out.println("扩展类加载器的加载路径:"+ System.getProperty("java.ext.dirs"));//C:\Program Files\Java\jdk1.8.0_92\jre\lib\ext
System.out.println("扩展类加载器的parent: "+ extensionLader.getParent());
 
6.2.4 自定义类加载器(先执行编译,可直接运行java源文件)
public class CompileClassLoader extends ClassLoader
{
	// 读取一个文件的内容
	private byte[] getBytes(String filename)
		throws IOException
	{
		File file = new File(filename);
		long len = file.length();
		byte[] raw = new byte[(int)len];
		try(
			FileInputStream fin = new FileInputStream(file))
		{
			// 一次读取class文件的全部二进制数据
			int r = fin.read(raw);
			if(r != len)
			throw new IOException("无法读取全部文件:"
				+ r + " != " + len);
			return raw;
		}
	}
	// 定义编译指定Java文件的方法
	private boolean compile(String javaFile)
		throws IOException
	{
		System.out.println("CompileClassLoader:正在编译 "
			+ javaFile + "...");
		// 调用系统的javac命令
		Process p = Runtime.getRuntime().exec("javac " + javaFile);
		try
		{
			// 其他线程都等待这个线程完成
			p.waitFor();
		}
		catch(InterruptedException ie)
		{
			System.out.println(ie);
		}
		// 获取javac线程的退出值
		int ret = p.exitValue();
		// 返回编译是否成功
		return ret == 0;
	}
	
	// 重写ClassLoader的findClass方法
	protected Class findClass(String name)
		throws ClassNotFoundException
	{
		Class clazz = null;
		// 将包路径中的点(.)替换成斜线(/)。
		String fileStub = name.replace("." , "/");
		String javaFilename = fileStub + ".java";
		String classFilename = fileStub + ".class";
		File javaFile = new File(javaFilename);
		File classFile = new File(classFilename);
		// 当指定Java源文件存在,且class文件不存在、或者Java源文件
		// 的修改时间比class文件修改时间更晚,重新编译
		if(javaFile.exists() && (!classFile.exists()
			|| javaFile.lastModified() > classFile.lastModified()))
		{
			try
			{
				// 如果编译失败,或者该Class文件不存在
				if(!compile(javaFilename) || !classFile.exists())
				{
					throw new ClassNotFoundException(
						"ClassNotFoundExcetpion:" + javaFilename);
				}
			}
			catch (IOException ex)
			{
				ex.printStackTrace();
			}
		}
		// 如果class文件存在,系统负责将该文件转换成Class对象
		if (classFile.exists())
		{
			try
			{
				// 将class文件的二进制数据读入数组
				byte[] raw = getBytes(classFilename);
				// 调用ClassLoader的defineClass方法将二进制数据转换成Class对象
				clazz = defineClass(name,raw,0,raw.length);
			}
			catch(IOException ie)
			{
				ie.printStackTrace();
			}
		}
		// 如果clazz为null,表明加载失败,则抛出异常
		if(clazz == null)
		{
			throw new ClassNotFoundException(name);
		}
		return clazz;
	}
	
	
	// 定义一个主方法
	public static void main(String[] args) throws Exception
	{
		// 如果运行该程序时没有参数,即没有目标类
		if (args.length < 1)
		{
			System.out.println("缺少目标类,请按如下格式运行Java源文件:");
			System.out.println("java CompileClassLoader ClassName");
		}
		// 第一个参数是需要运行的类
		String progClass = args[0];
		// 剩下的参数将作为运行目标类时的参数,
		// 将这些参数复制到一个新数组中
		String[] progArgs = new String[args.length-1];
		System.arraycopy(args , 1 , progArgs, 0 , progArgs.length);
		CompileClassLoader ccl = new CompileClassLoader();
		// 加载需要运行的类
		Class clazz = ccl.loadClass(progClass);
		// 获取需要运行的类的主方法
		Method main = clazz.getMethod("main" , (new String[0]).getClass());
		Object[] argsArray = {progArgs};
		main.invoke(null,argsArray);
	}
}

6.2.5 URLClassLoader

private static Connection conn;
// 定义一个获取数据库连接方法
public static Connection getConn(String url ,String user , String pass) throws Exception{
	if (conn == null){
		// 创建一个URL数组
		URL[] urls = {new URL("file:mysql-connector-java-5.1.30-bin.jar")};
		// 以默认的ClassLoader作为父ClassLoader,创建URLClassLoader
		URLClassLoader myClassLoader = new URLClassLoader(urls);
		// 加载MySQL的JDBC驱动,并创建默认实例
		Driver driver = (Driver)myClassLoader.loadClass("com.mysql.jdbc.Driver").newInstance();
		// 创建一个设置JDBC连接属性的Properties对象
		Properties props = new Properties();
		// 至少需要为该对象传入user和password两个属性
		props.setProperty("user" , user);
		props.setProperty("password" , pass);
		// 调用Driver对象的connect方法来取得数据库连接
		conn = driver.connect(url , props);
	}
	return conn;
}
public static void main(String[] args)throws Exception{
	System.out.println(getConn("jdbc:mysql://localhost:3306/mysql", "root" , "32147"));
}