zoukankan      html  css  js  c++  java
  • 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,那么在系统之中就会做出一个检测, 如果检测出了一个问题,则无法加载,会出现错误提示。
     
     
     
  • 相关阅读:
    MARKDOWN测试文章
    忘记网站登录密码实现快速查看
    IDEA debug下取消后续操作
    GitHub上传文件
    注解学习
    异常
    快捷键
    Android反编译三件套 apktool 、dex2jar、jd-gui
    ILSpy反编译工具之C#反汇编
    MAVEN配置及Spring Tool Suite的Maven配置
  • 原文地址:https://www.cnblogs.com/wzqjy/p/7819477.html
Copyright © 2011-2022 走看看