分析简单java类与反射的联系
web对反射的操作支持
在JSP之中有一种技术--javaBean。而且在jsp里面也配套有相应的操作方式,javaBean的核心在于简单java类,于是下面演示此操作
代码示例:创建一个简单的Student类
package com.hbsi.domain; public class Student { public Student() { System.out.println("构造方法执行了,对象实例化"); } private String name; private Integer age; private String sex; public String getName() { System.out.println("getName被调用"); return name; } public void setName(String name) { System.out.println("setName被调用"); this.name = name; } public Integer getAge() { System.out.println("getAge被调用"); return age; } public void setAge(Integer age) { System.out.println("setAge被调用"); this.age = age; } public String getSex() { System.out.println("getSex被调用"); return sex; } public void setSex(String sex) { System.out.println("setSex被调用"); this.sex = sex; } }
在web项目中所建立的每个java类最终都会保存在WEB-INF/classes目录下,所以classes就是一个CLASSPATH。但是要求是:
建立一个表单,输入完成后的数据需要设置到Student类中,同时再通过Student类对象取得输入内容
代码范例:定义一个输入表单
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>student_insert.jsp</title> </head> <body> <h1>纯菜鸟</h1> <form action="student_insert_do.jsp" method="post"> 姓名:<input type="text" name="name"><br/> 年龄:<input type="text" name="age"><br/> 性别:<input type="text" name="sex"><br/> <input type="submit" value="确定"> <input type="reset"><br/> </form> </body> </html>
此时Student类已经生成了对应的setter 和 getter方法,同时表单中的参数名与Student中的属性名一致,
代码范例:按照传统方式编写一个接受页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.hbsi.domain.Student"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <% request.setCharacterEncoding("UTF-8"); Student stu = new Student(); stu.setName(request.getParameter("name")); stu.setAge(Integer.parseInt(request.getParameter("age"))); stu.setSex(request.getParameter("sex")); %> <h1>Name:<%=stu.getName()%></h1> <h1>Age :<%=stu.getAge() %></h1> <h1>Sex :<%=stu.getSex() %></h1> </body> </html>
将此项目放到Tomcat中并且运行项目并输入,“懒蛋,18,男”执行结果如下
但是如果直接使用JSP的开发方式,以上的代码就可以简化了
代码范例编写简化后的JSP页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.hbsi.domain.Student"%> <% request.setCharacterEncoding("UTF-8"); %> <jsp:useBean id="stu" class="com.hbsi.domain.Student" scope="page"/> <jsp:setProperty property="*" name="stu"/> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <% request.setCharacterEncoding("UTF-8"); %> <h1>Name:<jsp:getProperty property="name" name="stu"/></h1> <h1>Age :<jsp:getProperty property="age" name="stu"/></h1> <h1>Sex :<jsp:getProperty property="sex" name="stu"/></h1> </body> </html>
执行程序并输入“懒蛋,19,jj” 运行结果如下
下面来看一下使用到的三个标签
1. 定义javaBean对象:
<jsp:useBean id="stu" class="com.hbsi.domain.Student" scope="page"/>
1. id:表示定义的实例化名字,Student stu = new Student();
2. class:对象的类型。此处是依靠反射实例化对象;(调用无参构造)
3. scope:此对象的保存范围:page,request,session,application
2. 设置属性内容:
<jsp:setProperty property="*" name="stu"/>
1. name:表示的要操作的对象名称,与id相同
2. property:要操作的属性名称,如果为* 表示自动操作,也可以指定,例如
<jsp:setProperty property="name" name="stu"/>
表示只操作name的赋值,参数名称一定要与属性名称一致(setter)
3. 取得属性内容:
<jsp:getProperty property="name" name="stu"/>
1. name:表示的要操作的对象名称,与id相同
2. property:取得输出的属性内容(getter)
但是此时的控制柜台就输出了
构造方法执行了,对象实例化
setName被调用
setAge被调用
setSex被调用
getName被调用
getAge被调用
getSex被调用
在Servlet之中自定义反射接收
对于WEB而言,发现“<jsp:useBean>”等标签只能在JSP页面使用,但是如果是一个真正开发,一定是使用Servlet进行的,所以现在就希望编写一个与之类似的功能,可以帮助我们实现此类操作。
本程序继续使用之前的Student类与student_insert.jsp页面,先完成一个专攻Student类操作的形式代码
代码范例:建立一个Servlet程序
package com.hbsi.servlet; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.annotation.WebServlet; /* by:wzq time:2017年11月12日 */ @WebServlet(name = "studentServlet", urlPatterns = { "/studentServlet" }) public class StudentServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); Class<?> cls = null; Object object = null; try { cls = Class.forName("com.hbsi.domain.Student"); object = cls.getDeclaredConstructor().newInstance();//获取实例化对象 Enumeration<String> parameterNames = request.getParameterNames();//获得全部参数名称 //判断是否有下一个参数 while(parameterNames.hasMoreElements()) { //获取下一个参数 String parameterName = parameterNames.nextElement(); //根据参数名得到参数值 String parameterValue = request.getParameter(parameterName); // System.out.println(parameterName+" "+parameterValue);//这里会显示输出前页面中参数名以及输入的参数值 //参数名就是对应的实体类Student中的属性名,所以根据这个参数名得到其类型 Field field = cls.getDeclaredField(parameterName); // System.out.println(field); String simpleName = field.getType().getSimpleName(); //取得setter方法。 Method setterMethod = cls.getMethod("set"+toUpperCaseOne(parameterName), field.getType()); //根据类型来选择的进行转换并赋值 switch (simpleName.toLowerCase()) { case "string": //设置值 setterMethod.invoke(object, parameterValue); break; case "int": case "integer": //设置值 setterMethod.invoke(object, Integer.parseInt(parameterValue)); break; case "date": break; case "double": break; case "char": break; case "character": break; } } } catch (Exception e) { e.printStackTrace(); } request.setAttribute("student", object); request.getRequestDispatcher("student_insert_do.jsp").forward(request, response); } //将字符串转换成大写开头的字母 public String toUpperCaseOne(String str) { return str.substring(0,1).toUpperCase().concat(str.substring(1).toLowerCase()); } protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); this.doGet(request, response); } }
页面代码范例:输出数据
<h1>Name:${student.name }</h1>
<h1>Age :${student.age }</h1>
<h1>Sex :${student.sex }</h1>
我们在表单页面输入“懒蛋,20,gg”,执行结果
但是如果将我们的所有的代码都放在Servlet类中进行处理的话,实在是过于麻烦,因为类的面向对象的设计原则是不同的组件完成不同功能。下面定义一个Bean操作的工具类
代码范例:定义一个工具类以实现属性的设置
package com.hbsi.BeanTools; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Enumeration; import javax.servlet.ServletRequest; import com.hbsi.stringUtils.StringTools; /* by:wzq time:2017年11月12日 */ public class BeanTools { /** * 这个方法只限制在WEB项目中使用, * 利用反射进行所有请求参数的设置,要求参数名称要与属性名称保持一致 * @param obj 要操作的实例化对象 * @param request HttpServletRequeat接口对象实例,目的是为了获得页面的输入参数 * @throws Exception */ public static void setValue(Object obj,ServletRequest request) throws Exception { Enumeration<String> parameterNames = request.getParameterNames();//获得全部参数名称 //判断是否有下一个参数 while(parameterNames.hasMoreElements()) { //获取下一个参数 String parameterName = parameterNames.nextElement(); //根据参数名得到参数值 String parameterValue = request.getParameter(parameterName); // System.out.println(parameterName+" "+parameterValue);//这里会显示输出前页面中参数名以及输入的参数值 //参数名就是对应的实体类Student中的属性名,所以根据这个参数名得到其类型 Field field = obj.getClass().getDeclaredField(parameterName); // System.out.println(field); String simpleName = field.getType().getSimpleName(); //取得setter方法。 Method setterMethod = obj.getClass().getMethod("set"+StringTools.toUpperCaseOne(parameterName), field.getType()); //根据类型来选择的进行转换并赋值 switch (simpleName.toLowerCase()) { case "string": //设置值 setterMethod.invoke(obj, parameterValue); break; case "int": case "integer": //设置值 setterMethod.invoke(obj, Integer.parseInt(parameterValue)); break; case "date": break; case "double": break; case "char": break; case "character": break; } } } }
上述代码中使用的StringTools代码示例:
package com.hbsi.stringUtils; /* by:wzq time:2017年11月12日 */ public class StringTools { //将字符串转换成大写开头的字母 public static String toUpperCaseOne(String str) { return str.substring(0,1).toUpperCase().concat(str.substring(1).toLowerCase()); } }
现在Servlet中的代码就相当简单了,代码示例
package com.hbsi.servlet; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.hbsi.BeanTools.BeanTools; import javax.servlet.annotation.WebServlet; /* by:wzq time:2017年11月12日 */ @WebServlet(name = "studentServlet", urlPatterns = { "/studentServlet" }) public class StudentServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); try { Class<?> cls = cls = Class.forName("com.hbsi.domain.Student"); Object object = cls.getDeclaredConstructor().newInstance();//获取实例化对象 BeanTools.setValue(object, request); request.setAttribute("student", object); request.getRequestDispatcher("student_insert_do.jsp").forward(request, response); } catch (Exception e) { e.printStackTrace(); } } protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); response.setContentType("text/html; charset=UTF-8"); this.doGet(request, response); } }
代码执行,表单输入“懒蛋,25,gr”
只要存在这样一个组件,那么在以后的开发里面就再也不用去编写接收数据,转型,设置属性等操作,但是整个操作的核心是一定要准备好实例化对象
除了针对于属性的操作可以通过反射简化代码之外,那么下面也可以运用反射进行解决Servlet重复的问题,几乎所有的Servlet都会存在以下的设计问题:
1. 需要接受的一个参数来区分操作类型,
2. 接收的状态参数(status)必须手工编写if else以区分执行那个方法
3. 到处充满了重复设置,sg,path属性的操作以及跳转代码。
解决操作状态的问题
1. 操作状态如果不接收,那么是无法进行操作的区分的,但是每次接受操作状态的时候都会感觉 到无奈。
状态例如:localhost:8080/tes/studentServlet/update 此时就要求的是更新操作
2. 在HttpServletRequest接口中定义了一个取得请求路径的方法: request.getRequestURI()
代码示例:取得状态码
package com.hbsi.servlet; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.hbsi.BeanTools.BeanTools; import javax.servlet.annotation.WebServlet; /* by:wzq time:2017年11月12日 */ @WebServlet(name = "employeeServlet", urlPatterns = { "/EmployeeServlet/*" }) public class EmployeeServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { String requestURI = request.getRequestURI(); System.out.println(requestURI);//请求全路径 int lastIndexOf = requestURI.lastIndexOf("/");//取得最后一个/的位置 String substring = requestURI.substring(lastIndexOf+1); System.out.println(substring); } protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { this.doGet(request, response); } }
请求路径:http://localhost:8080/tes/EmployeeServlet/update
执行结果:update
对于所有的操作方法上都可以发现,操作的状态就是方法名称
代码示例:解决代码调用
package com.hbsi.servlet; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.annotation.WebServlet; /* by:wzq time:2017年11月13日 */ @WebServlet(name = "dispatchaerServlet", urlPatterns = { "/DispatchaerServlet/*" }) public class DispatchaerServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { String requestURI = request.getRequestURI(); //取得最后一个/的位置 int lastIndexOf = requestURI.lastIndexOf("/"); //状态信息 String status = requestURI.substring(lastIndexOf+1); System.out.println(status);//输出状态信息 //得到本类的所有方法 Method[] declaredMethods = this.getClass().getDeclaredMethods(); //循环判断状态信息与本类的方法名是否一致 for (int i = 0; i < declaredMethods.length; i++) { if(declaredMethods[i].getName().equals(status)) { //如果一直获得参数类型 Class<?>[] parameterTypes = declaredMethods[i].getParameterTypes(); try { //根据参数类型得到其方法 Method method = this.getClass().getMethod(status, parameterTypes); //调用方法 method.invoke(this, request,response); } catch (Exception e) { e.printStackTrace(); } } } } public void insert(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { System.out.println("执行插入方法"); } public void delete(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { System.out.println("执行删除方法"); } public void update(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { System.out.println("执行更新方法"); } protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { this.doGet(request, response); } }
执行结果:insert 执行插入方法
在上述代码中method.invoke(this, request,response);需要区分参数是比较麻烦的,而且必须是需要匹配好后才可以调用
以上的代码调用严格来讲算是一种习惯,因为各个方法里面需要有request,response等对象但是也同时感觉到,麻烦程度很高,比如需要知道参数的个数。
如果不想做参数的判断,如果说现在像insert(),update()等操作,那些地方可能使用到request或者response,接收参数,跳转,
代码优化:
package com.hbsi.servlet; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.annotation.WebServlet; /* by:wzq time:2017年11月13日 */ @WebServlet(name = "dispatchaerServlet", urlPatterns = { "/DispatchaerServlet/*" }) public class DispatchaerServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { String uri = "/errors.jsp"; String requestURI = request.getRequestURI(); //取得最后一个/的位置 int lastIndexOf = requestURI.lastIndexOf("/"); //状态信息 String status = requestURI.substring(lastIndexOf+1); try { Method method = this.getClass().getMethod(status); uri = method.invoke(this).toString(); } catch (Exception e) { e.printStackTrace(); } request.getRequestDispatcher(uri).forward(request, response); } public String insert(){ System.out.println("执行插入方法"); return "/insert.jsp"; } public String delete() { System.out.println("执行删除方法"); return "/delete.jsp"; } public String update() { System.out.println("执行更新方法"); return "/update.jsp"; } protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException { this.doGet(request, response); } }
执行结果:执行插入方法
此时Servlet已经可以反射调用,而且也可以反射将表单的参数设置给属性。
总结:关于反射设置数据内容的情况
代码示例:定义三个类,Emp,Dept,Company,三个类相关联
Emp> Dept > Company
代码实现功能:可以深层的为对象的属性赋值,
EMP属性:
private Integer empid; private String empname; private Dept dept = new Dept();
Dept属性:
private Integer deptid; private String deptname; private Company company = new Company();
Company属性:
private Integer companyid; private String companyname;
工具类StringFirstUPTools:
//作用就是将制定字符串的首字母大写处理 public static String stringFirstUP(String str) { return str.substring(0, 1).toUpperCase()+str.substring(1,str.length()); }
核心代码实现:注意不适用于自身赋值
package com.hbsi.servlet; import java.lang.reflect.Field; import java.lang.reflect.Method; import com.hbsi.domain.Emp; /* by:wzq time:2017年11月18日 */ public class Servlet { //emp属性操作需要当前类对象完成 //emp.dept属性需要由emp类对象完成 private String requestParamName = "emp.dept.deptid";//模拟多级赋值的操作的参数 private String value = "6666"; private Emp emp = new Emp();//必须有实例化对象 public Emp getEmp() { return emp; } public void handleParam(String paramName,String value) throws Exception { String[] split = paramName.split("\."); Object currentObj = this; for (int i = 0; i < split.length; i++) { //根据拆分后的字符串得到相应的getter方法 Method method = currentObj.getClass().getMethod("get"+StringFirstUPTools.stringFirstUP(split[i])); if(i<split.length-1) {//判断是否为最后一个对象而非属性 currentObj = method.invoke(currentObj);//将得到的下一个对象赋值到currentObj } } //根据属性名称得到属性对象 Field declaredField = currentObj.getClass().getDeclaredField(split[split.length-1]); Class<?> type = declaredField.getType();//属性类型 String typeName = type.getSimpleName();//属性类型简单名称 //根据属性名及类型得到对应的setter方法 Method declaredMethod = currentObj.getClass().getDeclaredMethod("set"+StringFirstUPTools.stringFirstUP(split[split.length-1]), type); //根据类型来选择的进行转换并赋值 switch (typeName.toLowerCase()) { case "string": declaredMethod.invoke(currentObj, String.valueOf(value)); break; case "int": case "integer": declaredMethod.invoke(currentObj, Integer.parseInt(value)); break; case "double": declaredMethod.invoke(currentObj, Double.valueOf(value)); break; } } public void get() {//模拟请求 try { this.handleParam(this.requestParamName,this.value); } catch (Exception e) { e.printStackTrace(); } //验证 Integer companyid = this.getEmp().getDept().getDeptid(); System.out.println(companyid); } public static void main(String[] args) { new Servlet().get(); } }
执行结果 : 6666
使用此类操作可以无限制进行数据的赋值,排除自关联的情况
ClassLoader类
当使用java命令解释程序的时候,会自动根据CLASSPATH加载所需要的类文件,而用户现在要也可以编写自己的类加载器。
在Class类里面默认定义有专门的取得一个类的类加载器的方法
取得类加载器:public ClassLoader getClassLoader() 返回类的类加载器。
public class ClassLoderDemo { public static void main(String[] args) { ClassLoderDemo classLoderDemo = new ClassLoderDemo(); System.out.println(classLoderDemo.getClass().getClassLoader()); System.out.println(classLoderDemo.getClass().getClassLoader().getParent()); System.out.println(classLoderDemo.getClass().getClassLoader().getParent().getParent()); } }
执行结果
jdk.internal.loader.ClassLoaders$AppClassLoader@7960847b //系统类加载器 jdk.internal.loader.ClassLoaders$PlatformClassLoader@2f333739 //系统类加载器的父类加载器 null
自己也可以自定义一个类加载器,只需要继承 ClassLoader即可,此类定义如下
public abstract class ClassLoader extends Object 类加载器是负责加载类的对象。
ClassLoader的子类只需要重写loadClass方法就可以进行类加载的实现
代码示例:做一个默认的类加载器
public class MyClassLoader extends ClassLoader{ @Override public Class<?> loadClass(String arg0) throws ClassNotFoundException { return super.loadClass(arg0);//直接调用父类的loadClass()方法 } public static void main(String[] args) throws Exception { MyClassLoader myClassLoader = new MyClassLoader(); Class<?> loadClass = myClassLoader.loadClass("java.util.Date");//与Class.forName()作用几乎一样 Object newInstance = loadClass.getDeclaredConstructor().newInstance(); System.out.println(newInstance);//Sun Nov 19 11:51:29 CST 2017 } }
上述程序只是继承并直接是调用了父类中的方法,与使用Class.forName()作用几乎一样,因为都是默认实现,下面使用自定义的类加载器加载指定文件
代码示例:创建一个Student类,并且把Student编译后的class文件复制到D盘根目录下,
package com.hbsi.domain; public class Student { public Student() { System.out.println("student 实例化"); } @Override public String toString() { return "Student 学生信息 toString()"; } }
实现自定义类加载器,---加载文件数据
package com.hbsi.servlet; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class ClassLoderDemo extends ClassLoader{ public static void main(String[] args) { try { ClassLoderDemo classLoderDemo = new ClassLoderDemo(); classLoderDemo.loadClassFile("com.hbsi.domain.Student");//参数必须是包点类名 } catch (Exception e) { e.printStackTrace(); } } public void loadClassFile(String className) { try { byte[] loadFile = this.loadFile(className); //defineClass() 将字节数组转换为类别 Class的实例 Class<?> defineClass = super.defineClass(className, loadFile, 0, loadFile.length); Object newInstance = defineClass.getDeclaredConstructor().newInstance();//实例化 System.out.println(newInstance);//输出调用toString } catch (Exception e) { e.printStackTrace(); } } public byte[] loadFile(String className) throws Exception{ String[] split = className.split("\.");//拆分 String name = split[split.length-1];//取的Class类名 File file = new File("D:/"+name+".class");//拼接路径 //内存输出流 ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); //字节流 InputStream inputStream = new FileInputStream(file); byte date[] = new byte[1024];//缓冲区 int len = 0; while((len = inputStream.read(date))!=-1) { arrayOutputStream.write(date, 0, len); } //将内存中的数据取出 byte[] byteArray = arrayOutputStream.toByteArray(); arrayOutputStream.close(); inputStream.close(); return byteArray; } }
Student 学生信息 toString()
在编写自定义类加载器的时候有如下要求
1. 不要重写loadClass方法,因为此方法会使会使用部分默认系统类加载器
2. 如果是手工加载方式,则可以使用方法进行转换:
1. protected final Class<?> defineClass(String name, byte[] b,int off,int len) throws ClassFormatError 将字节数组转换为类别Class的实例。
此时的类加载的方式可能发生变化,那么就可以将程序继续扩大,直接利用HTTP协议加载,我们将Student.class文件拷贝到WEB目录下,这样就成为了WEB资源,如果要访问,必须基于HTTP协议完成。如果进行网络加载,就必须使用URL类,在java.net包中,如下代码,
package com.hbsi.BeanTools; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; public class ClassLoderDemo extends ClassLoader{ public static void main(String[] args) { try { ClassLoderDemo classLoderDemo = new ClassLoderDemo(); classLoderDemo.loadClassFile("com.hbsi.domain.Student");//参数必须是包点类名 } catch (Exception e) { e.printStackTrace(); } } public void loadClassFile(String className) { try { byte[] loadFile = this.loadFile(className); //defineClass() 将字节数组转换为类别 Class的实例 Class<?> defineClass = super.defineClass(className, loadFile, 0, loadFile.length); Object newInstance = defineClass.getDeclaredConstructor().newInstance();//实例化 System.out.println(newInstance);//输出调用toString } catch (Exception e) { e.printStackTrace(); } } public byte[] loadFile(String className) throws Exception{ String[] split = className.split("\.");//拆分 String name = split[split.length-1];//取的Class类名 URL url = new URL("http://localhost:8080/tes/"+name+".class"); URLConnection openConnection = url.openConnection(); //内存输出流 ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream(); //字节流 InputStream inputStream = openConnection.getInputStream(); byte date[] = new byte[1024];//缓冲区 int len = 0; while((len = inputStream.read(date))!=-1) { arrayOutputStream.write(date, 0, len); } //将内存中的数据取出 byte[] byteArray = arrayOutputStream.toByteArray(); arrayOutputStream.close(); inputStream.close(); return byteArray; } }
以及Student.class的存放目录
执行结果:
student 实例化
Student 学生信息 toString()
在进行类加载的过程之中存在一种概念 -- 双亲加载:如果是系统类则会由系统的类加载器进行加载,例如如果现在加载的是java.lang.String,那么这个类会在程序运行时自动完成加载,而后如果用户再以同样的名称加载了自定义的java.lang.String,那么在系统之中就会做出一个检测, 如果检测出了一个问题,则无法加载,会出现错误提示。