类型转换在JavaWeb中的应用浅析
0.引言
Java Web在软件开发中具有非常重要的地位,开发人员在软件开发过程中通常会采用MVC的设计模式,即将程序分为视图层、控制层和模型层。这就意味着,数据将在MVC三层中进行交互,而在交互过程中不可避免的会发生数据间的类型转换。Java是强数据类型的编程语言,要求参与运算的所有量都必须有明确的数据类型[1],因此不同数据类型之间不能随意地进行转换。探讨Java中不同数据类型间转换原理以及方法有助于提高开发人员的开发效率,同时对转换过程中可能出现的错误或异常进行分析,能够提早归避程序异常,提高软件的鲁棒性。
1.类型转换的介绍
类型转换是将某种数据类型的数据(数值、表达式的值或引用)赋值给不同于初始类型的变量。在实际开发中,我们可以根据不同的需求将数据类型转换分为:
①基本数据类型间的转换;
②引用数据类型间的转换;
③基本数据类型与引用数据类型间的相互转换。
1.1基本数据类型间的转换
在Java中基本数据类型包括数值型、布尔类型、字符型。 Java设置了这些类型的大小,这些大小并不随着机器结构的变化而变化。Java中的基本数据类型及所分配大小如表1所示。
表1 基本数据类型所分配字节表
类型 | byte | short | int | long | float | double | boolean | char |
---|---|---|---|---|---|---|---|---|
字节 | 1 | 2 | 4 | 8 | 4 | 8 | - | 2 |
需要说明的是,boolean数据类型只有true和false两个可能的值,其值用于表示真或假,值的“大小”并不是精确定义的。因此,在Java基础数据类型之间,boolean与其他七种基本数据类型都不能进行转换。
对于其他七种数据类型所分配的字节数大小关系为byte <(short=char)< int < long < float < double,它们之间进行类型转换可以分为拓宽数据类型转换和缩窄数据数据类型转换,其中缩窄数据类型转换可能会造成精度损失或者其他一些变化。
(1)拓宽数据类型转换
拓宽数据类型转化是指将所占字节数少的基础数据类型转换为所占字节数多的其他基础数据类型,以将byte转换为short为例:short的高八位将用零进行填充,低八位拷贝byte的值,具体转换示意图如图所示。
由此可知,将所占字节数少的变量的值赋值到所占字节数多的类型的变量时,数据会依次存放到赋值的变量的低位中,高位用零补齐。
(2)缩窄数据类型转换
缩窄数据类型转换是指将所占字节数多的基础数据类型转换为所占字节数少的其他基础数据类型,以将short转换为byte为例:byte的值拷贝short的低八位,short的高八位忽略不计。具体转换示意图如图所示。
由此可知,将所占字节数多的变量的值赋值到所占字节数少的类型的变量时,数据会依次存放到赋值的变量的低位中,高位用零补齐。从低位截取相应的数据(按当前小类型位数进行截取)赋值给相应字节数少的变量,这时就可能会发生精度损失。
另外,由于Java在编译过程中能够检测到精度损失以及不同基础数据类型(数值型和字符型)之间的转换,因此转换过程中需要进行强制类型转换。
double a=45.5;
byte b=(byte)a;
char c= (char) b;
1.2引用数据类型间的转换
Java中引用数据类型间的转换分为:①向上类型转换②向下类型转换。
(1)向上类型转换
Java中向上类型转换就是把子类对象直接赋值给父类对象,如 Father obj = new Son()。
将子类对象转为父类对象时,属于小类型转大类型,可以直接进行类型转换,不需要进行强制转换。
需要注意的是,向上转型成功后,obj可以调用Father(父类类型)中的成员和方法,但是不能调用Son(子类型)中新增的方法和属性;若父类的方法被子类重写,则向上转型后调用的方法为子类重写的方法。
(2)向下类型转换
与向上类型转换相反,向下类型转换指的是子类对象指向父类引用,如Son son= (Son) father。
需要注意的是,向下类型转换需要通过强制类型转换,且father必须是son或其子类的引用,否则程序会抛出ClassCastException(类型转换异常)。
假设Student是Person的子类, Person person = new Person();Student student = (Student)person;运行时会抛出异常,因为person指向不是student或其子类的引用。
1.3基本数据类型与引用数据类型间的转换
Java中的基本数据类型与引用数据类型之间的转换主要指基本数据类型与其所对应的包装类之间的相互转换,如int和Integer之间的互转。
(1)基础数据类型转包装类型
以int转为Integer为例,在JDK中java.lang包下的Integer类中有public Integer(int value)的构造方法。在将int类型数据赋值给Integer类的对象时,会隐式调用该方法,从而实现基本数据类型向引用数据类型的转换。
Integer integer = 200;
其他基础数据类型与之类似。
(2)包装类型转基础类型
以Integer转为int为例,在JDK中java.lang包下的Integer类中有在public int intValue(),在将Integer类的对象赋值给int类型变量时会隐式调用该方法,返回Integer对象所对应的int值,从而实现引用数据类型向基本数据类型的转换。
此外,除char和boolean以为外的6种数值类型的包装类还提供了将字符串转为基础数据类型的静态方法public static xxx parseXxx(String s),以String转为int为例,其转换示例代码如下。
Int a= Integer.parseInt(“2022”);
需要注意的是,如果字符串中包含不可解析为数字的字符时,会抛出NumberFormatException(数字格式化异常)。
2.类型转换在Java Web中的应用与存在问题
类型转换在Java Web开发过程中使用相当频繁,根据MVC模型的业务处理流程进行分析,类型转换可能出现可能出现在如图3所示的业务流程中:
图3 MVC业务流程图
①控制层接收数据并进行相应的类型转换
②模型层接收控制层数据并进行处理
③控制层接收模型层数据并进行存储
④视图层从域对象中读取数据并显示
2.1接收数据并进行类型转换
视图层(view)将请求数据通过表单等方式传输到控制层,控制层通过javax.servlet.http.HttpServletRequest类中的String getParameter(String name)方法来获取参数名为name的参数值。从该方法的API可知,该方法以字符串的形式返回请求参数的值,如果参数不存在,则返回null。
这时就可能需要将String类型转换为所需的其他基础数据类型,如:
String ageStr=req.getParameter(“age”);//获取年龄
Integer age=Integer.parseInt(ageStr);
由于返回值可能为null或包含不可解析为数字的字符,因此可能抛出NullPointerException(空指针异常)、NumberFormatException(数值类型格式异常)等。
2.2接收控制层数据并进行处理
控制层在获取到相应的参数后,可能会将请求数据封装成一个JavaBean对象并传输到模型层。在程序开发中,为了提高程序的重用性通常会采用多态,这是就可能会出现数据类型转换。
例如控制层获得了圆的半径r,并将其封装为一个Circle对象并将其传入模型层计算其面积。在开发过程中,通常会将图形(Shape)及面积计算方法(calArea)抽象为父类,则在控制层可能存在以下代码:
Shape shape = new Circle®;//向上类型转换
UserUtil.printArea();
在模型层可能存在以下代码:
public static void printArea(Shape shape){
Circle circle = (Circle)shape; //向下类型转换
System.out.println(circle.calArea());}
但由于在向下类型转换中,不能确定shape能否强制类型转换为Circle,因此可能会抛出ClassCastException(类型转换异常)。
2.3获取模型层数据并存入域对象中
在Java Web开发中,经常要进行数据交互,即将处理完成的数据存入域对象中,来维护客户端和服务器之间关联。以会话技术(session)为例,将数据存入Session域中,将使用javax.servlet.http.HttpSession接口中的void setAttribute(String key, Object value) 方法。
由于模型层或控制层处理完成的数据类型可能为任意类型,而数据存入方法的第二个参数是Object类型,因此存入数据时可能发生数据类型转换。需要说明的是,这时发生的数据类型转换往往是向上转型,不会抛出运行时异常。
2.4从域对象中读取数据并显示
当需要将域对象中的数据取出时,将会使用到Object getAttribute(String key)方法,该方法所取出的数据类型为Object。然而,在开发过程中往往会根据需要或存入时的数据类型,将取得的数据进行强制类型转换。以Session为例,将存入Session域中的用户信息取出,示例代码如下:
HttpSession session=request.getSession();
User user = (User) session.getAttribute(“user”);
由于不确定所取出的数据类型是否能强制类型转换为目标数据类型,因此可能会抛出ClassCastException(类型转换异常);另外,从Servlet API可知,当该方法中所要取出的key不存在时,将返回null,因此也可能会抛出NullPointerException(空指针异常)。
3.数据转换异常的解决办法
经过上述分析可知,在Java Web开发过程中数据间类型转换时可能会产生异常包括ClassCastException、NullPointerException、NumberFormatException等。这些异常都属于运行时异常,即编译器无法直接检测,但在运行过程中可能会发生,因此在开发过程中需要尽可能的避免。
3.1 类型转换异常
ClassCastException(类型转换异常)是进行强制类型转换时产生的异常,当某个对象强制执行向下转型,但该对象又不是目标类型及其子类实例的引用,将引发该异常。这类异常的处理方法可以采用:
①使用try catch语句手动捕获异常
②instanceof运算符
在进行强制类型转换时,使用instanceof运算符判断父类引用指向的对象是否为目标类型及其子类的实例,如:
if (shape instanceof Circle circle ){
System.out.println(circle.calArea());}
③getClass()
通过反射机制获取当前需要进行强制类型转换的实例的完整类名,并与目标类类型的类名进行比较。如果两者相同就进行类型转换,示例代码如下:
if(person.getClass().getName().equals(Student.class.getName() )){Student student = (Student)person;}
3.2数值类型格式异常
NumberFormatException(数值类型格式异常)是将字符串转换为数值类型时,由于该字符串不具有相应的数字格式时所抛出的异常,此类异常的处理方法可以采用:
①使用try catch语句手动捕获异常
②在进行数据类型转换之前,使用正则表达式进行验证,如将字符串str转换为数值类型的number:
String regex=" ([1-9]\d*.?\d*)|(0.\d*[1-9])";
if(str.matches(regex)){
double number = Double.parseDouble (str);}
3.3空指针异常
NullPointerException(空指针异常)是访问空值(null)对象的属性或者调用其方法时所抛出的异常,此类异常的处理方法可以采用:
①使用try catch语句手动捕获异常
②对对象进行非空判断
在Java Web开发过程中涉及到对象的属性访问或方法调用时,在这之前增加非空判断,以确保改对象不为null,然后再执行后续操作,如比对user中的password和数据密码dbPassword是否一致:
if(user!=null&&user.getPassword()!=null&&user.getPassword.equals(dbPassword)){/to do other/}
③其他
为了防止空指针异常,在开发过程中可以将确定为非null的对象放在equals()方法之前,例如:
if(“zhangsan”.equals(request.getParameter(“username”))){/to do other/}
除此之外,在视图层显示数据信息时可以使用EL表达式来代替Java代码,如使用${user.username}来代替<%=((user)request.getSession().getAttribute(“user”)).getUsername()%>。
4.结束语
类型转换在实际开发中应用相当广泛,本文就类型转换在Java Web中的应用为例,对类型转换时可能出现的问题进行了较为全面的分析和探讨,并总结出了对应的方法来避免上述问题。从MVC开发模式入手,探讨数据在各个模块之间进行传递时可能发生的数据转换异常,目的在于希望为开发人员提供参考,从而提高开发效率,增加程序的健壮性。
sion().getAttribute(“user”)).getUsername()%>。
4.结束语
类型转换在实际开发中应用相当广泛,本文就类型转换在Java Web中的应用为例,对类型转换时可能出现的问题进行了较为全面的分析和探讨,并总结出了对应的方法来避免上述问题。从MVC开发模式入手,探讨数据在各个模块之间进行传递时可能发生的数据转换异常,目的在于希望为开发人员提供参考,从而提高开发效率,增加程序的健壮性。