java--分析简单java类与反射的联系

分析简单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);
}
}

 请求路径:http://localhost:8080/tes/DispatchaerServlet/insert

执行结果: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 实例化
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,那么在系统之中就会做出一个检测, 如果检测出了一个问题,则无法加载,会出现错误提示。
 
 
 
原文地址:https://www.cnblogs.com/wzqjy/p/7819477.html