首页 > 编程知识 正文

java异常类的定义,java自定义数据类型

时间:2023-05-03 15:05:25 阅读:11904 作者:796

为什么要定制类加载器

类加载机制: http://www.cn blogs.com/xrq 730/p/4844915.html

类加载器: http://www.cn blogs.com/xrq 730/p/4845144.html

这两篇文章详细介绍了类加载器和类加载器,但最后剩下的问题是定制类加载器。 为什么要定制班级加载器? 因为Java为用户提供了许多类加载器,但与实际使用相比缺乏功能。 举个例子吧。 主流的Java Web服务器(如Tomcat )实现了定制的类加载器。 由于是功能性Web服务器,因此需要解决以下问题:

1 .部署在同一台服务器上的两个web APP应用程序所使用的Java类库可以相互隔离。 这是最基本的要求。 两个不同的APP应用程序可能依赖于同一第三方类库的不同版本。 一个服务器不能只请求一个类库。 此外,服务器还必须确保两个APP应用程序的类库可以相互使用

2 .部署在同一台服务器上的两个web APP应用程序所使用的Java类库可以相互共享。 这种需求也很常见。 例如,同一Spring类库中的10个APP应用程序存储在隔离目录中,这些目录不能单独存储在每个APP应用程序中

3、支持热插拔。 虽然我们知道JSP文件最终必须编译为. class文件才能在虚拟机上运行,但由于纯文本存储的特性,在运行时更改的概率比第三方类库或自己的. class文件更高

由于存在这些问题,Java为用户提供的ClassLoader将无法满足需要。 Tomcat服务器有自己的ClassLoader体系结构。 当然,这是基于父母的委托模式。

JDK的类加载器

在实现自己的类加载器之前,让我们先看看JDK的类加载器是如何实现的。

1受保护的同步类? 加载类(字符串名称,布尔解析) )。

2 throwsclassnotfoundexception3{4//first,checkiftheclasshasalreadybeenloaded5class c=findloadedclass { name }; 6if(c==null )7try ) 8if(Parent!=null } { 9c=parent.load class (name,false ); 10 ) else(11c=findbootstrapclass0) name; 12 ) 13 ) catch(classnotfoundexceptione ) {14 //If still not found,theninvokefindclassinorder 15//to find the class.16c=21 ) 22返回; 23 )方法原理很简单。 试着一步一步地说明一下吧。

第1、5行,首先查找是否加载了. class

第2、第~12行,如果没有加载过. class文件,则去查找加载程序的父加载程序。 如果父加载器不是null,则执行父加载器的loadClass方法,并继续提交类加载请求,直到父加载器为null (引导类加载器)

3、在第13-17行中,父加载器开始尝试加载. class文件,加载成功后返回java.lang.Class,加载失败后抛出ClassNotFoundException,然后加载子加载器

4、第19行~第21行,分析此. class文件时,请进行分析。 虽然分析角色类加载的文章中也有写,但主要是将符号引用直接替换为引用的过程

让我们来看看findClass这个方法:

保护类? 查找类(字符串名称) throwsclassnotfoundexception ) thrownewclassnotfoundexception (字符串名称) name; }是的,没有具体实现。 只是扔了一个异常,而且是protected。 这充分证明了。 这个方法是用来重写给开发者的。

自定义类加载器

对java.lang.ClassLoader的load class (字符串名称,布尔解析)方法的分析得出以下两个结论:

p>

1、如果不想打破双亲委派模型,那么只需要重写findClass方法即可

2、如果想打破双亲委派模型,那么就重写整个loadClass方法

当然,我们自定义的ClassLoader不想打破双亲委派模型,所以自定义的ClassLoader继承自java.lang.ClassLoader并且只重写findClass方法。

第一步,自定义一个实体类Person.java,我把它编译后的Person.class放在D盘根目录下:

1package com.xrq.classloader;

2 3 public class Person 4 { 5 private String name; 6 7 public Person() 8 { 9 10 }11 12 public Person(String name)13 {14 this.name = name;15 }16 17 public String getName()18 {19 return name;20 }21 22 public void setName(String name)23 {24 this.name = name;25 }26 27 public String toString()28 {29 return "I am a person, my name is " + name;30 }31 }

第二步,自定义一个类加载器,里面主要是一些IO和NIO的内容,另外注意一下 defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class----只要二进制字节流的内容符合Class文件规 范。我们自定义的MyClassLoader继承自java.lang.ClassLoader,就像上面说的,只实现findClass方法:

public class MyClassLoader extends ClassLoader

{ public MyClassLoader() { } public MyClassLoader(ClassLoader parent) { super(parent); } protected Class<?> findClass(String name) throws ClassNotFoundException { File file = getClassFile(name); try { byte[] bytes = getClassBytes(file); Class<?> c = this.defineClass(name, bytes, 0, bytes.length); return c; } catch (Exception e) { e.printStackTrace(); } return super.findClass(name); } private File getClassFile(String name) { File file = new File("D:/Person.class"); return file; } private byte[] getClassBytes(File file) throws Exception { // 这里要读入.class的字节,因此要使用字节流 FileInputStream fis = new FileInputStream(file); FileChannel fc = fis.getChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel wbc = Channels.newChannel(baos); ByteBuffer by = ByteBuffer.allocate(1024); while (true) { int i = fc.read(by); if (i == 0 || i == -1) break; by.flip(); wbc.write(by); by.clear(); } fis.close(); return baos.toByteArray(); }}

第三步,Class.forName有一个三个参数的重载方法,可以指定类加载器,平时我们使用的Class.forName("XX.XX.XXX")都是使用的系统类加载器Application ClassLoader。写一个测试类:

1public class TestMyClassLoader

2 { 3 public static void main(String[] args) throws Exception 4 { 5 MyClassLoader mcl = new MyClassLoader(); 6 Class<?> c1 = Class.forName("com.xrq.classloader.Person", true, mcl); 7 Object obj = c1.newInstance(); 8 System.out.println(obj); 9 System.out.println(obj.getClass().getClassLoader());10 }11 }

看一下运行结果:

I am a person, my name is nullcom.xrq.classloader.MyClassLoader@5d888759

个人的经验来看,最容易出问题的点是第二行的打印出来的是"sun.misc.Launcher$AppClassLoader"。造成这个问题的关键在于MyEclipse是自动编译的,Person.java这个类在ctrl+S保存之后或者在Person.java文件不编辑若干秒后,MyEclipse会帮我们用户自动编译Person.java,并生成到CLASSPATH也就是bin目录下。在CLASSPATH下有Person.class,那么自然是由Application ClassLoader来加载这个.class文件了。解决这个问题有两个办法:

1、删除CLASSPATH下的Person.class,CLASSPATH下没有Person.class,Application ClassLoader就把这个.class文件交给下一级用户自定义ClassLoader去加载了

2、TestMyClassLoader类的第5行这么写"MyClassLoader mcl = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent());", 即把自定义ClassLoader的父加载器设置为Extension ClassLoader,这样父加载器加载不到Person.class,就交由子加载器MyClassLoader来加载了

 

ClassLoader.getResourceAsStream(String name)方法作用

ClassLoader中的getResourceAsStream(String name)其实是一个挺常见的方法,所以要写一下。这个方法是用来读入指定的资源的输入流,并将该输入流返回给用户用的,资源可以是图像、声音、.properties文件等,资源名称是以"/"分隔的标识资源名称的路径名称。

不仅ClassLoader中有getResourceAsStream(String name)方法,Class下也有getResourceAsStream(String name)方法,它们两个方法的区别在于:

1、Class的getResourceAsStream(String name)方法,参数不以"/"开头则默认从此类对应的.class文件所在的packge下取资源,以"/"开头则从CLASSPATH下获取

2、ClassLoader的getResourceAsStream(String name)方法,默认就是从CLASSPATH下获取资源,参数不可以以"/"开头

其实,Class的getResourceAsStream(String name)方法,只是将传入的name进行解析一下而已,最终调用的还是ClassLoader的getResourceAsStream(String name),看一下Class的getResourceAsStrea(String name)的源代码:

1 public InputStream getResourceAsStream(String name) { 2 name = resolveName(name); 3 ClassLoader cl = getClassLoader0(); 4 if (cl==null) { 5 // A system class. 6 return ClassLoader.getSystemResourceAsStream(name); 7 } 8 return cl.getResourceAsStream(name); 9 }10 11 private String resolveName(String name) {12 if (name == null) {13 return name;14 }15 if (!name.startsWith("/")) {16 Class c = this;17 while (c.isArray()) {18 c = c.getComponentType();19 }20 String baseName = c.getName();21 int index = baseName.lastIndexOf('.');22 if (index != -1) {23 name = baseName.substring(0, index).replace('.', '/')24 +"/"+name;25 }26 } else {27 name = name.substring(1);28 }29 return name;30 }

代码不难,应该很好理解,就不解释了。

.class和getClass()的区别

最后讲解一个内容,.class方法和getClass()的区别,这两个比较像,我自己没对这两个东西总结前,也常弄混。它们二者都可以获取一个唯一的java.lang.Class对象,但是区别在于:

1、.class用于类名,getClass()是一个final native的方法,因此用于类实例

2、.class在编译期间就确定了一个类的java.lang.Class对象,但是getClass()方法在运行期间确定一个类实例的java.lang.Class对象

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。