美高梅平台下载-美高梅娱乐平台登录

热门关键词: 美高梅平台下载,美高梅娱乐平台登录

反射也都可以动态的去调用该对象中的属性和方

日期:2019-10-07编辑作者:美高梅平台下载

什么是java反射呢?简单的说,对于java中任意一个类,利用反射都可以获取该类中的所有属性和方法,而对于一个java对象,反射也都可以动态的去调用该对象中的属性和方法。

一、什么是反射

 一、什么是Servlet

对于正在学习Servlet的童鞋来说,最痛苦的无疑是对请求参数的处理,譬如:对请求参数的类型转换,繁琐的setter方法和getter方法等。这样无疑是 大大的降低了开发效率。

1、编译和运行

在了解什么是Java反射机制前,先聊聊Java的编译和运行。

还记得第一次使用记事本编写第一个Java程序的场景吗?通过命令窗口,使用javac命令编译一个.java文件,生成一个.class文件,再使用java命令运行.class文件。

在编译期,jvm会去检查.java文件中使用的类、类的属性和方法、类的对象等信息,如果都能找到对应的信息,则编译通过,否则会编译报错或抛出异常信息。

在运行期,会根据.class文件信息,处理其中的代码逻辑,如果遇到程序运行错误会抛出异常信息。

下面我们使用一个小例子来说明编译和运行。新建一个Animal类,根据传入的参数不同,创建不同的对象,执行不同的方法。

public class Animal{
    public static void main(String[] args) {
        if("Cat".equals(args[0])){
            Cat cat = new Cat();
            cat.eat();
        }
        if("Dog".equals(args[0])){
            Dog dog = new Dog();
            dog.eat();
        }   
    }
}

使用cmd命令编译时,会报错,找不到Cat类和Dog类。

E:Java项目实战>javac Animal.java
Animal.java:6: 错误: 找不到符号
                        Cat cat = new Cat();
                        ^
  符号:   类 Cat
  位置: 类 Animal
Animal.java:6: 错误: 找不到符号
                        Cat cat = new Cat();
                                      ^
  符号:   类 Cat
  位置: 类 Animal
Animal.java:11: 错误: 找不到符号
                        Dog dog = new Dog();
                        ^
  符号:   类 Dog
  位置: 类 Animal
Animal.java:11: 错误: 找不到符号
                        Dog dog = new Dog();
                                      ^
  符号:   类 Dog
  位置: 类 Animal
4 个错误

这时我们新建一个Cat类,编译Cat类后,再次编译Animal类,又报了两个错误,找不到Dog类。

Animal.java:11: 错误: 找不到符号
                        Dog dog = new Dog();
                        ^
  符号:   类 Dog
  位置: 类 Animal
Animal.java:11: 错误: 找不到符号
                        Dog dog = new Dog();
                                      ^
  符号:   类 Dog
  位置: 类 Animal

如果我们想要使用猫类,而不管狗类是否存在,那该怎么做?那就是绕过编译期,在运行期动态执行猫类的方法。怎样绕过编译期呢?这就需要使用Java的反射机制了。

    Servlet 运行在服务端的Java小程序,

美高梅娱乐平台登录 1

2、反射的概念

反射是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法。这种动态获取信息以及动态调用对象方法的功能,我们称之为Java语言的反射机制

继续上面遗留的问题,在不新建一个Dog.java文件时,怎样调到Cat类的eat方法?

首先,我们要根据传入的参数动态加载类,然后创建类的实例对象,最后执行类的方法。因为你无法确定传入的参数是猫类还是狗类,就需要定义一个超类或接口,让猫类继承超类或实现接口,重载或重写eat()方法,在创建类实例时,只用创建超类或接口的实例即可。

public class AnimalBetter {
    public static void main(String[] args) {
        try {
            Class c1 = Class.forName(args[0]);
            IAnimal animal = (IAnimal)c1.newInstance();
            animal.eat();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这里我们选用的是接口

public interface IAnimal {
    public void eat();  
}

猫类的实现

public class Cat implements IAnimal {
    public void eat() {
        System.out.println("Cat is eatting");
    }
}

    是sun公司提供一套规范(接口)

而对于struts来说,我想大家也会很好奇为什么我们只需要将表单的name属性设置成className.fieldName后,struts就可以完成自动封装吧。

二、反射的优缺点

使用反射,可以在代码改动最小的情况下有效扩展功能。就像上面的小例子,如果哪天我们想使用老虎类进行eat动作,我们还需要改动AnimalBetter类的代码吗?答案是不需要,只需要新建一个老虎类,实现IAnimal的接口,重写eat方法,就可以了,对其他既有代码无需做任何改动。

使用反射,可以动态的创建类实例和使用类的方法,可以分析类文件,可以访问类的私有属性或方法,对于一些需要在运行时处理的事情都可以交给反射实现。

既然反射这么好,为什么不在项目中大范围的使用?其实,反射也是有弊端的。反射包括了一些动态类型,jvm无法对这些代码进行优化,实际操作效率低于非反射操作,在那些经常被执行的代码或对性能要求较高的模块尽量少使用反射。反射在程序有安全限制的环境中是无法使用的。反射可以访问私有的属性或方法,可能会破坏封装性,产生意料之外的结果。

 

美高梅娱乐平台登录 2

三、如何使用反射

    主要功能:

当然,这都归功于java的反射机制

1、使用过程

首先要获取操作的类的Class对象,然后通过Class类中的方法获取和查看该类中的方法和属性,最后使用反射API来操作这些信息。

    用来处理客户端请求

1.1 使用传统的代码编写一个注册Servlet

实体类:Student.class

public class Student { private String name; private Integer age; private Date register; ....setter getter...

Servlet : StudentServlet.class

@WebServlet(name="studentServlet",urlPatterns={"/StudentServlet"})public class StudentServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding; Student student = new Student(); Date date = null; String name = request.getParameter; Integer age = Integer.parseInt(request.getParameter; String dateString = request.getParameter("register"); try { date = new SimpleDateFormat("yyyy-MM-dd").parse(dateString); } catch (ParseException e) { e.printStackTrace(); } student.setName; student.setAge; student.setRegister; System.out.println; }}

可以看到,每当我们发送一个请求时,都需要手动的去对请求参数进行数据类型转换,然后在封装到对应的javaBean中去。当我们使用反射机制的使用,这些重复的代码就完全可以被抽离出来。

2、获取要操作的类的Class对象

有三种方式获取类的Class对象

(1)已经知道该类的类名

Class c2 = Animal.class;

实际上,任何一个类都有一个隐含的静态成员变量class,并且任何一个类都是Class类的实例对象,这个对象我们称为该类的类类型。

(2)已经知道该类的对象,通过getClass方法获取

Animal animal = new Animal();
Class c3 = animal.getClass();

不管是上面的c2还是这里的c3,都是Animal类的类类型,一个类只可能是Class类的一个实例对象或一个类类型。

(3)使用Class类中的forName()静态方法

Class c4 = Class.forName("Animal"); //forName(String str)中参数为类的全路径(包名+类名)

    响应给浏览器的动态资源

1.2 使用反射自动封装

实体类操作工具: BeanUtil .class

public class BeanUtil { /** * 利用反射对javabean对象进行属性封装(参数名和属性名要保持一致) * @param obj 需要封装的对象 * @param request 从request中获取请求参数 * @param datepatern 日期转换的格式 */ public static void setValue(Object obj, ServletRequest request, String datepatern) throws Exception{ Class<?> cls = obj.getClass(); // 1 获取所有的请求参数 Enumeration<String> parameterNames = request.getParameterNames(); while(parameterNames.hasMoreElements{ // 2 获取每个参数名 String paramName = parameterNames.nextElement(); // 3 获取参数对应的值 String paramValue = request.getParameter(paramName); // 4 获取参数对应的属性 Field field = cls.getDeclaredField(paramName); // 5 获取属性对应的简单类型 String fieldTypeName = field.getType().getSimpleName(); // 6 获取对应的方法 Method method = cls.getDeclaredMethod("set" + StringUtil.initCap(paramName), field.getType; // 7 对参数类型进行转换,执行对应的setter方法 if("String".equalsIgnoreCase(fieldTypeName)){ method.invoke(obj, paramValue); }else if("integer".equalsIgnoreCase(fieldTypeName) || "int".equals(fieldTypeName)){ method.invoke(obj, Integer.parseInt(paramValue)); }else if("double".equalsIgnoreCase(fieldTypeName)){ method.invoke(obj, Double.parseDouble(paramValue)); }else if("date".equalsIgnoreCase(fieldTypeName)){ method.invoke(obj, new SimpleDateFormat(datepatern).parse(paramValue)); } } }}

首字母大写转换:StringUtil.class

public class StringUtil { /** * 首字母大写 * @param str * @return */ public static String initCap(String str){ return str.substring .toUpperCase().concat(str.substring.toLowerCase; }}

Servlet : StudentServler.class

@WebServlet(name="studentServlet",urlPatterns={"/StudentServlet"})public class StudentServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding; Student student = new Student(); try { BeanUtil.setValue(student, request, "yyyy-MM-dd"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println; }}

在使用了反射后,我们可以动态的去对请求的参数进行处理,对其封装,不在需要我们去手动的处理。

当然咯,这只是一个简单属性的封装,其中还有一些需要优化的部分。后期会给大家实现struts2的复杂javaBean封装功能、action方法的选用以及自动转发、重定向等。

3、获取类的属性和方法

美高梅娱乐平台登录 ,我们将其编写为一个工具类,如下:

/**
 * 获取类的属性和方法
 * @author 小川94
 * @date 2018年6月24日
 */
public class ClassUtil {

    /**
     * 获取类的成员变量信息
     * @param obj 要操作的类
     */
    public static void getClassFieldInfo(Object obj){
        // 获取要操作的类的Class对象
        Class c = obj.getClass();
        // 获取该类的成员变量信息
        // field类是java.lang.reflect包下的类,封装了关于类成员变量的操作
        Field[] fs = c.getFields(); // 获取的是所有的public的成员变量的信息
        Field[] fs2 = c.getDeclaredFields(); // 获取的是该类自己声明的成员变量的信息
        // 使用foreach遍历成员变量
        for (Field field : fs2) {
            // 得到成员变量的类型的类类型
            Class fieldType = field.getType();
            // 获取类类型的名称
            String typeName = fieldType.getName();
            // 获取成员变量的名称
            String fieldName = field.getName();
            System.out.println(typeName + " " + fieldName);
        }
    }

    /**
     * 获取类的方法信息
     * @param obj 要操作的类
     */
    public static void getClassMethodInfo(Object obj){
        // 获取要操作的类的Class对象
        Class c = obj.getClass();
        // 获取要操作的类的名称
        System.out.println("类的名称是:" + c.getName());
        // 获取该类的方法信息
        // Method类是java.lang.reflect包下的类,封装了关于类方法的操作
        Method[] ms = c.getMethods(); //获取的是所有的public的函数,包括从父类继承而来的
        Method[] ms2 = c.getDeclaredMethods(); // 获取的是所有该类自己声明的方法,不限访问权限
        // 遍历方法
        for (Method method : ms2) {
            // 得到方法的返回值类型的类类型
            Class returnType = method.getReturnType();
            System.out.print(returnType.getName() + " ");
            // 获取方法名
            System.out.print(method.getName() + " (");
            // 获取参数类型,得到的是参数列表的类型的类类型
            Class[] para = method.getParameterTypes();
            for (Class class1 : para) {
                System.out.print(class1.getName() + ",");
            }
            System.out.println(")");
        }
    }

    /**
     * 获取类的构造方法
     * @param obj 要操作的类
     */
    public static void getClassConstructorMethodInfo(Object obj){
        // 获取要操作的类的Class对象
        Class c = obj.getClass();
        // 获取该类的构造方法信息
        // constructor类是java.lang.reflect包下的类,封装了关于类的构造方法的操作
        Constructor[] cs = c.getConstructors(); // 获取所有的public的构造函数
        Constructor[] cs2 = c.getDeclaredConstructors(); //得到所有的构造函数
        for (Constructor constructor : cs2) {
            System.out.print(constructor.getName()+"(");
            // 获取构造函数的参数列表,得到的是参数列表的类类型
            Class[] paramTypes = constructor.getParameterTypes();
            for (Class class1 : paramTypes) {
                System.out.print(class1.getName()+",");
            }
            System.out.println(")");
        }
    }
}

编写测试类,如下:

public class Test {
    public static void main(String[] args) {
        System.out.println("----- 成员变量信息 -----");
        ClassUtil.getClassFieldInfo(new Integer(2));
        System.out.println("----- 构造方法信息 -----");
        ClassUtil.getClassConstructorMethodInfo(new Integer(2));
        System.out.println("----- 方法信息 -----");
        ClassUtil.getClassMethodInfo(new Integer(2));
    }
}

测试结果如下,大家可以将测试结果与Integer这个类做比较。

----- 成员变量信息 -----
int MIN_VALUE
int MAX_VALUE
java.lang.Class TYPE
[C digits
[C DigitTens
[C DigitOnes
[I sizeTable
int value
int SIZE
int BYTES
long serialVersionUID
----- 构造方法信息 -----
java.lang.Integer(int,)
java.lang.Integer(java.lang.String,)
----- 方法信息 -----
类的名称是:java.lang.Integer
int numberOfLeadingZeros (int,)
int numberOfTrailingZeros (int,)
int bitCount (int,)
boolean equals (java.lang.Object,)
java.lang.String toString (int,int,)
java.lang.String toString ()
java.lang.String toString (int,)
int hashCode (int,)
int hashCode ()
int min (int,int,)
int max (int,int,)
int reverseBytes (int,)
int compareTo (java.lang.Integer,)
int compareTo (java.lang.Object,)
byte byteValue ()
short shortValue ()
int intValue ()
long longValue ()
float floatValue ()
double doubleValue ()
java.lang.Integer valueOf (java.lang.String,int,)
java.lang.Integer valueOf (int,)
java.lang.Integer valueOf (java.lang.String,)
java.lang.String toHexString (int,)
int compare (int,int,)
java.lang.Integer decode (java.lang.String,)
void getChars (int,int,[C,)
int reverse (int,)
int stringSize (int,)
int sum (int,int,)
int parseInt (java.lang.String,)
int parseInt (java.lang.String,int,)
long toUnsignedLong (int,)
int compareUnsigned (int,int,)
int divideUnsigned (int,int,)
int formatUnsignedInt (int,int,[C,int,int,)
java.lang.Integer getInteger (java.lang.String,java.lang.Integer,)
java.lang.Integer getInteger (java.lang.String,int,)
java.lang.Integer getInteger (java.lang.String,)
int highestOneBit (int,)
int lowestOneBit (int,)
int parseUnsignedInt (java.lang.String,)
int parseUnsignedInt (java.lang.String,int,)
int remainderUnsigned (int,int,)
int rotateLeft (int,int,)
int rotateRight (int,int,)
int signum (int,)
java.lang.String toBinaryString (int,)
java.lang.String toOctalString (int,)
java.lang.String toUnsignedString (int,)
java.lang.String toUnsignedString (int,int,)
java.lang.String toUnsignedString0 (int,int,)

        

4、使用反射API来操作类

public class MethodDemo {

    public static void main(String[] args) {
        // 第一种写法
        try {
            // 获取要操作的类的Class对象
            Class c = Class.forName("sessionone.Foo");
            // 获取print方法
            Method m = c.getMethod("print", String.class);
            // 第二种写法,new Class[]{}里面可以接多个参数,也可以为空
            Method m2 = c.getMethod("print", new Class[]{String.class});
            // 第三种写法,获取该类自己声明的方法
            Method m3 = c.getDeclaredMethod("print", String.class);
            // 执行方法
            m3.invoke(c.newInstance(), "小川94");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 第二种写法
        Foo foo = new Foo();
        foo.print("小川94");
        // 第三种写法
        try {
            Class c2 = Class.forName("sessionone.Foo");
            Foo foo2 = (Foo) c2.newInstance();
            foo2.print("小川94");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 第四种写法
        try {
            Foo foo3 = new Foo();
            Class c3 = Class.forName("sessionone.Foo");
            Method method = c3.getDeclaredMethod("print", String.class);
            method.invoke(foo3, "小川94");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Foo{
    public void print(String name){
        System.out.println("Hello," + name);
    }
}

     servlet的实质就是java代码,

四、反射的实际应用

    通过java的API动态的向客户端输出内容

1、在JDBC中的使用

Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url, user, password);

在装载驱动时,使用了反射,在getConnection的源代码中,使用了反射,整个JDBC都和反射有关联。

 

2、在集合泛型中的使用

ArrayList list = new ArrayList();
ArrayList<String> list2 = new ArrayList<String>();
System.out.println(list.equals(list2)); //true

Class c = list.getClass();
System.out.println("list的类类型名称:"+c.getName());
Class c2 = list2.getClass();
System.out.println("list2的类类型名称:"+c2.getName());
System.out.println(c == c2); //true

// c1==c2结果返回true,说明编译之后集合的泛型是去泛型化的
// Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
// list2.add(20); 这样写,是会编译报错
try {
    System.out.println("list2中有"+list2.size()+"个元素");
    Method m = c2.getMethod("add", Object.class);
    m.invoke(list2, 20);
    System.out.println("list2中有"+list2.size()+"个元素");
    System.out.println(list2.toString()); //[20],已经将类型是整型的数添加到list2中
} catch (Exception e) {
    e.printStackTrace();
} 

集合中的泛型只是为了防止编译错误,在运行时会将泛型全部清除。

    以后写的程序就不在是在本地执行了。

3、在Servlet中的使用

在之前的servlet系列文章中,使用了反射来完成了一个Servlet处理多个请求,具体可以看那篇文章的详细描述。

    而是编译成字节码

4、在框架中的使用

在主流框架中都有反射的影子,通过配置、注解完成一些需要静态加载类的方法转为使用反射来动态加载类,具体的会在讲解到框架时细讲。

    放到服务器上来去执行。

 

    编写程序时, 不需要有main函数了。

    因为写完后

    就把编写的程序编译成字节码,

    放到服务器上面

    当前发送就一个请求的时候

    服务器就会按照一定规则调用编写的代码

 

二、Servlet快速入门

    1.创建一个web工程名为:MyServlet

    2.在JavaResource中src下

       创建一个包名称为com.myxq.servlet

    3.在创建的servlet包当中

       创建一个class文件起名为FirstServlet

    4.进入该class实现一个Servlet接口,

       实现它未实现的方法

       重点看service方法

       在该方法当中写入一句话进行输出

       

   美高梅娱乐平台登录 3

    5.在web.xml当中进行配置

      

     美高梅娱乐平台登录 4

 

    6.启动服务器访问:

       localhost:8080/MyServlet/firstServlet

       就会自动执行创建的servlet中service方法

 

三、servlet内部机制

        想要弄清楚这个问题

        就必须得要先了解什么反射

 

四、类的加载时机

本文由美高梅平台下载发布于美高梅平台下载,转载请注明出处:反射也都可以动态的去调用该对象中的属性和方

关键词:

括号中多个命令之间用分号隔开,EL表达式作用

EL表达式作用: 向浏览器输出域对象中的变量或表达式计算结果。用来替换掉jsp表达式 一、shell编程中条件表达式的...

详细>>

将json格式多级菜单保存到数据库

/** * 将json菜单保存到数据库 * * @return */ @RequestMapping("save/menu") @ResponseBody public MapString, Object menu() { String jsonStr = "{n"...

详细>>

【美高梅娱乐平台登录】开发出来的项目需要部

7.运行结果访问 从上面可以看出来,jar包运行的时候会404错误,因为默认jsp不会被拷贝进来,而war包里面有包含了j...

详细>>

因为需要使用到这方面内容,starters可以简化Sp

本项目旨在把SpringBoot的特性展示出来,都是开发中经常用到的,可以留作SpringBoot使用备忘录,持续更新中。 原创作...

详细>>