类加载、对象映射、class文件结构分析和运行

基本知识点

  • 基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
  • 每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。 

       

Class类的常用方法

1、getName() 
 (类、接口、数组类、基本类型或 void)名称。   

2、newInstance()
   为类创建一个实例,newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。   

3、getClassLoader() 
    返回该类的类加载器。   

4、getComponentType() 
    返回表示数组组件类型的 Class。   

5、getSuperclass() 
    返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。   

6、isArray() 
    判定此 Class 对象是否表示一个数组类。

Class的一些使用技巧


    1、forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象。例如
    Object obj = Class.forName(s).newInstance();

    2、虚拟机为每种类型管理一个独一无二的Class对象。因此可以使用==操作符来比较类对象。例如:
    if(e.getClass() == Employee.class)...

双亲委托模型

类加载器:自顶向下加载,自底向上校验判断是否加载

防止重复加载

resolveClass 类的解析

defineClass 加载字节码生成Class对象

loadClass双亲委托模型的展示可自定义实现

findClass加载字节码生成Class对象  自定义加载器必须实现的

 类加载器jar包卸载

URLClassLoader 1.7之后有close方法

类加载器实例应用

JavaSPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。

DriverManager通过ServiceLoader加载
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();

被加载的类S

对象实例化S p = service.cast(c.newInstance());【Object——》S类型,校验及转换】

多线程和类加载器

package practise;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class CustomClassLoader extends ClassLoader {

	/** 类名 **/
	private String name;

	/** 通过构造方法设置父类加载器和要热加载的类名 **/
	public CustomClassLoader(ClassLoader parent, String name) {
		super(parent);
		if (name == null || name.length() <= 0)
			throw new NullPointerException();

		this.name = name;
	}

	@Override
	protected Class<?> loadClass(String name, boolean resolve)
			throws ClassNotFoundException {
		Class<?> clazz = null;
		/** 如果是我们想要热加载的类则调用我们重写的findClass方法来加载 **/
		if (this.name.equals(name) && !"java".equals(name)) {
			/** 先看看要热加载的类之前是否已经加载过了,因为一个类加载器只能加载一个类一次,加载多次会报异常 **/
			clazz = findLoadedClass(name);
			/** clazz==null说明之前没有加载过 **/
			if (clazz == null)
				clazz = findClass(name);
			
			System.out.println(Thread.currentThread().getName());
			/**
			 * 类的生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析统称为连接 如果要连接类
			 */
			if (resolve)
				resolveClass(clazz);// 如果类已连接过,resolveClass方法会直接返回
			return clazz;
		}
		return super.loadClass(name, resolve);
	}

	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		String fileName = c2f(name);
		byte[] bytes = f2b(fileName);
		return defineClass(name, bytes, 0, bytes.length);
	}

	/**
	 * 类名转为文件名
	 * 
	 * @param name
	 * @return
	 */
	private String c2f(String name) {
		/** 编译后的class文件存放的目录 **/
		String baseDir = "D:\\workspace\\practise\\bin\\";
		name = name.replace(".", File.separator);
		name = baseDir + name + ".class";
		return name;
	}

	/**
	 * 读取文件byte数组
	 * 
	 * @param fileName
	 * @return
	 */
	private byte[] f2b(String fileName) {
		RandomAccessFile file = null;
		FileChannel channel = null;
		byte[] bytes = null;
		try {
			/** 随机存取文件对象,只读取模式 **/
			file = new RandomAccessFile(fileName, "r");
			/** NIO文件通道 **/
			channel = file.getChannel();
			/** NIO字节缓冲 **/
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			int size = (int) channel.size();
			bytes = new byte[size];
			int index = 0;
			/** 从NIO文件通道读取数据 **/
			while (channel.read(buffer) > 0) {
				/** 字节缓冲从写模式转为读取模式 **/
				buffer.flip();
				while (buffer.hasRemaining()) {
					bytes[index] = buffer.get();
					++index;
				}
				/** 字节缓冲的readerIndex、writerIndex置零 **/
				buffer.clear();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (channel != null) {
				try {
					channel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (file != null) {
				try {
					file.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return bytes;
	}

	/**
	 * 热加载类
	 * 
	 * @return
	 */
	public Class<?> loadClass() {
		try {
			return loadClass(name);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}

	public static void main(String[] args) throws NoSuchMethodException,
			SecurityException, IllegalArgumentException,
			InvocationTargetException, ClassNotFoundException, FileNotFoundException, InterruptedException {
//		BufferedReader in=new BufferedReader(new FileReader("gc.log"));
//		FilterInputStream filterInputStream=new DataInputStream(new FileInputStream(""));
//		StringBufferInputStream stringBufferInputStream=mew StringBufferInputStream("123");
		byte []arrayBytes=new byte[50];
//		ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(arrayBytes);
		/** 要进行热加载的类名 **/
		String name = "practise.Dictionary";
		HashMapP<String, String> printer = null;
		/*Constructor<?> segmentConstructor = HashMapP.Entry.class
				.getConstructor(int.class, String.class,String.class,
						 HashMapP.Entry.class);
		segmentConstructor.setAccessible(true);*/
		System.out.println("输入任意字符进行热加载,直接敲回车键退出程序");
		try {
			ClassLoader appLoader=Thread.currentThread().getContextClassLoader();
			Class<?> clazz1 = appLoader.loadClass("practise.HashMapP");
			printer=(HashMapP<String, String>) clazz1.newInstance();
			printer.put("123", "123");
			System.out.println(printer);
//			appLoader.loadClass(name);
			CustomClassLoader loader = new CustomClassLoader(appLoader, name);
			
			//loader.loadClass();
			Class<?> clazz = loader.loadClass("practise.Dictionary");
			/**
			 * 被子加载器加载的类拥有被父加载器加载的类的可见性 Printer是由自定义类加载器加载的,
			 * 而它的父类IPrinter是由系统类加载器加载的, 因此IPrinter对于Printer具有可见性,
			 * 因此转型成功,并不会因为类加载器不同导致ClassCastException异常
			 */
			Constructor<?> constructor = clazz.getConstructor();
			constructor.setAccessible(true);
			// AccessibleObject[] array;
			// constructor.setAccessible(array, true);
			//都是可访问状态
//			Dictionary map = (Dictionary) constructor
//					.newInstance();
//			/** 看看是否热加载成功了 **/
//			System.out.println(map);
			System.out.println(clazz.getClassLoader().toString());
			Thread thread=new Thread(new Runnable() {
                
                @Override
                public void run() {
                    ClassLoader loader=Thread.currentThread().getContextClassLoader();
                    System.out.println(loader.toString());
                    Dictionary dictionary=new Dictionary<>();
//new实例依然有当前AppClassLoader加载
                    System.out.println(dictionary.getClass().getClassLoader().toString());
                }
            });
			thread.setContextClassLoader(loader);
			Thread.sleep(1000*3);
			thread.start();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

	}
	
	
}

双亲委派模型的破坏者

DriverManager在获取外部jdbc中Driver实现类对象时,根加载器是使用不到应用加载器的类,因此通过Thread.getContextLoader上下文加载器得到,即也就是破坏了双亲委派模型,类之间是相互隔离的

几个api获取区别

  1. getSystemClassLoader:当前应用类加载器,就是classpath目录下
  2. Thread.getContextLoader:是线程设置的类加载器,默认为父线程的类加载,例如tomcat获取到是WebAppClassLoader,而非ApplicationClassLoader

QA

同一个字节码第二次加载会报异常

类加载、jar加载太多,如何卸载

参考文献

http://blog.csdn.net/javazejian/article/details/73413292

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ArcGIS是一款常用的地理信息系统软件,它供了丰富的功能和工具来处理和分析地理数据。在ArcGIS中,要素类是地理数据的基本组织形式之一。 要素类是由相同类型的要素组成的集合,每个要素都具有相同的属性结构。要素可以是点、线、面等地理实体。要素类可以包含多个要素,这些要素可以表示不同的地理实体,如建筑物、道路、河流等。 要素类之间的转换可以通过ArcGIS中的工具和功能来实现。以下是一种常见的方法来介绍ArcGIS中要素类至要素类的转换: 1. 打开ArcGIS软件并要素类:首先,打开ArcGIS软件并包含要素类的地理数据集。可以通过导航到文件路径或连接到数据库来要素类。 2. 选择要素类转换工具:在ArcGIS中,有多种工具可用于要素类之间的转换。例如,可以使用"Feature Class to Feature Class"工具将一个要素类转换为另一个要素类。 3. 配置转换参数:在选择要素类转换工具后,需要配置一些参数来定义转换的方式。这些参数可能包括输入要素类、输出要素类、字段映射等。 4. 运行转换工具:配置完参数后,可以运行转换工具来执行要素类至要素类的转换。在转换过程中,ArcGIS会根据配置的参数将输入要素类转换为输出要素类。 5. 检查转换结果:转换完成后,可以检查输出要素类以确保转换结果符合预期。可以使用ArcGIS中的地图查看器或属性表来查看和分析输出要素类。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenxuezhou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值