JavaBean组件

Posted shi_zi_183

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaBean组件相关的知识,希望对你有一定的参考价值。

JavaBean组件

在软件开发时,一些数据和功能需要在很多地方使用,为了方便将他们进行“移植”,Sun公司提出一种JavaBean技术,使用JavaBean可以对这些数据和功能进行封装,做到“一次编写,到处运行”。本章将针对JavaBean、发射、内省、JSP标签访问JavaBean及BeanUtils工具。

初始JavaBean

什么是JavaBean
JavaBean是Java开发语言中一个可以重复使用的软件组件,它本质就是一个Java类,为了规范JavaBean的规范,Sun公司发布了JavaBean的规范,它要求一个标准的JavaBean组件需要遵循一定的编码规范,
1、他必须具有一个公共的无参的构造方法,这个方法可以编译器自动产生的默认构造方法。
2、它提供公共的setter方法和getter方法让外部程序设置和获取JavaBean的属性。
例:Book.java

package JavaBean;

public class Book {
	private double price;
	public double getPrice(){
		return price;
	}
	public void setPrice(double price){
		this.price=price;
	}
}

访问JavaBean的属性
在讲解面向对象时,经常会使用类的属性,类的属性指的是类的成员变量。在JavaBean中同样也有属性,但是他和成员变量不是一个概念,它是以方法的形式出现的,这些方法必须遵循一定的命名规范,例如,在JavaBean中包含一个String类型的属性name,那么在JavaBean中必须至少包含getName()和setName()方法中的一个,这两个方法的声明如下所示:

public String getName();
public void setName(String name);

1、getName()方法:称为getter方法或者属性访问器,该方法以小写的get前缀开始,后跟属性名,属性名的第一个字母要大写。
2、setName()方法:称为setter方法或者属性修改器,该方法必须以小写set前缀开始,后跟属性名,属性名的第一个字母要大写。
如果一个属性只有getter方法,则该属性为只读属性,如果一个属性只有setter方法,则该属性为只读属性,如果一个属性既有getter方法,又有setter方法,则该属性为读写属性。通常来说,在开发时,其属性都定义为读写属性。
需要注意的是,对于JavaBean属性的命名方式有一个例外情况,如果属性的类型为boolean,他的命名方式应该使用is/get而不是set/get。例如,有一个boolean类型的属性married,该属性所对应的方法:

public boolean isMarried();
public void setMarried(boolean married);

setter方法命名方式没有变化,而getter方法变成了isMarried。

反射

在Java中反射式极其重要的知识,在后期接触的大量框架的底层都运用了发射技术,因此掌握反射技术将帮助我们更好地理解这些框架地底层原理,以便灵活地掌握框架地使用。
认识Class类
Java反射地源头是class类,若想完成反射操作,首先必须认识Class类。一般情况下,需要现有一个类地完整路径引入之后,才可以按照固定地格式产生实例化对象,但是在Java中允许通过一个实例化对象找到一个类的完整信息,这就是Class类的功能。
GetClassNameDemo.java

package JavaBean;
class X{}
public class GetClassNameDemo {
	public static void main(String[] args){
		X x=new X();
		System.out.println(x.getClass().getName());
	}
}

在这里插入图片描述
可以看到命令行输出了完整的包名。
例中,对象引用x调用了getClass()方法,该方法是从Object类中继承而来的。

public final Class<?>getClass()

从上述定义可以看出,该方法返回值的类型是Class类。这是因为Java中Object类是所有类的父类,所以,任何类的对象都可以通过调用getClass()方法转变为Class类型来表示。需要注意的是,在定义Class类时使用了泛型声明,若想避免程序出现警告信息,可以在泛型中指定操作的具体类型。
Class类表示一个类的本身,通过Class可以完整地得到一个类中的结构,包括此类中的方法定义、属性定义等。

方法声明功能描述
static Class<?>forName(String className)返回与带有给定字符串名的类或接口相关联的Class对象
Constructor<?>[]getConstructors()返回一个包含某些Constructor对象的数组,这些对象反映此Class对象所表示类的所有公共构造方法
Field[] getDeclaredField(String name)返回包括某些Field对象的数组,这些对象反映此Class对象所表示的类或接口所声明的所有字段。包括公共、保护、默认访问和私有字段,但不包括继承的字段
Field[] getFields()返回一个包含某些Fields对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共字段,包括继承的公共字段
Method[] getMethods()返回一个包含某些Method对象的数组,这些对象反映此Class对象所表示的类或接口的公共成员方法。
Method[] getMethod(String name,Class<?>…parameterTypes)返回一个Method对象,反映此Class对象所表示的类或接口的指定公共成员方法
Class<?>[]getInterfaces()返回该类所实现的接口的一个数组。确定此对象所表示的类或接口实现的接口
String getName()以String的形式返回此Class对象所表示的实体名称
Package getPackage()获取此类的包
Class <?super T> getSuperclass()返回此Class所表示的实体的超类的Class
T newInstancs()创建此Class对象所表示类的一个新实例
boolean isArray()判定此Class对象是否表示一个数组类

在Class类中本身没有定义非私有的构造方法,因此不能通过new直接创建Class类的实例。
1、通过“对象.getClass()”方法获取该对象的Class实例。
2、通过Class类的静态方法forName(),用类的全路径名获取一个Class实例
3、通过"类名.class"的方式来获取Class实例,对于基本数据类型的封装类,还可以采用,TYPE来获取相对应的基本数据类型的Class实例。
需要注意的是,通过Class类的forName()方法相比其他两种方法更灵活,其他两种方法都需要明确一个类,如果一个类操作不确定时,使用起来可能会受一些限制。但是forName()方法只需以字符串的方式传入即可。

通过反射创建对象

当使用构造方法创建对象时,构造方法可以是有参的也可以是无参的。同样,通过反射创建对象的方式也有两种,就是调用有参和无参的构造方法。
1、通过无参构造方法实例化对象
如果想通过Class类本身实例化其他对象就可以使用newInstance()方法,但是必须要保证被实例化的类中存在一个无参构造方法。
ReflectDemo01.java

package JavaBean;

class Person{
	private String name;
	private int age;
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name=name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age=age;
	}
	public String toString(){
		return "姓名:"+this.name+" 年龄:"+this.age;
	}
}

public class ReflectDemo01 {
	public static void main(String[] args)throws Exception{
		Class clazz=Class.forName("JavaBean.Person");
		Person p=(Person)clazz.newInstance();
		p.setName("李芳");
		p.setAge(18);
		System.out.println(p);
	}
}

在这里插入图片描述

2、通过有参构造方法实例化对象
当通过有参构造方法实例化对象时,需要分为三个步骤完成
1、通过Class类的getConstructors()方法取得本类中的全部构造方法。
2、向构造方法中传递一个对象数组进去,里面包含构造方法中所需的各个参数。
3、通过Constructor类实例化对象。
需要注意的是,Constructor类表示的是构造方法,该类有很多常用的方法

方法声明功能描述
int getModifiers()获取构造方法的修饰符
String getName()获取构造方法的名称
Class<?>[]getParameterTypes()获取构造方法中参数的类型
T newInstance(Object…initargs)向构造方法中传递参数,实例化对象

ReflectDemo01.java

package JavaBean;

import java.lang.reflect.Constructor;

class Person{
	private String name;
	private int age;
	public Person(String name,int age){
		this.name=name;
		this.age=age;
	}
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name=name;
	}
	public int getAge(){
		return age;
	}
	public void setAge(int age){
		this.age=age;
	}
	public String toString(){
		return "姓名:"+this.name+" 年龄:"+this.age;
	}
}

public class ReflectDemo01 {
	public static void main(String[] args)throws Exception{
		Class clazz=Class.forName("JavaBean.Person");
		Constructor cons[]=clazz.getConstructors();
		Person p=(Person)cons[0].newInstance("李芳",30);
		System.out.println(p);
	}
}

在这里插入图片描述
通过反射访问属性
通过反射不仅可以创建对象,还可以访问属性。在反射机制中,属性的操作是通过Filed类实现的,它提供的set()和get()方法分别用于设置和获取属性。需要注意的是,如果访问的属性是私有的,则需要在使用set()或get()方法前,使用Firld类中的setAccessible()方法将需要操作的属性设置成可以被外界访问的。
ReflectDemo01.java

package JavaBean;

import java.lang.reflect.Field;

class Person{
	private String name;
	private int age;
	public String toString(){
		return "姓名:"+this.name+" 年龄:"+this.age;
	}
}

public class ReflectDemo01 {
	public static void main(String[] args)throws Exception{
		Class clazz=Class.forName("JavaBean.Person");
		Object p=clazz.newInstance();
		Field nameField=clazz.getDeclaredField("name");
		nameField.setAccessible(true);
		nameField.set(p, "李四");
		Field ageField=clazz.getDeclaredField("age");
		ageField.setAccessible(true);
		ageField.set(p, 20);
		System.out.println(p);
	}
}

在这里插入图片描述
通过反射调用方法
当获得某个类对应的Class对象后,就可以通过Class对象的getMethods()方法或getMethod()方法获取全部方法或者指定方法,getMethod()方法和getMethods()这两个方法的返回值,分别是Method对象和Method对象数组。每个Method对象都对应一个方法,程序可以通过获取Method对象来调用对应的方法。在Method里包含一个invoke()方法

public Object invoke(Object obj,Object...args)

在上述方法定义中,参数obj是该方法最主要的参数,他后面的参数args是一个相当于数组的可变参数,用来接收传入的实参。
ReflectDemo01.java

package JavaBean;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

class Person{
	private String name;
	private int age;
	public String sayHello(String name,int age){
		return "大家好,我是"+name+",今年"+age+"岁!";
	}
}

public class ReflectDemo01 {
	public static void main(String[] args)throws Exception{
		Class clazz=Class.forName("JavaBean.Person");
		Method md=clazz.getMethod("sayHello", String.class,int.class);
		String result=(String)md.invoke(clazz.newInstance(), "张三",35);
		System.out.println(result);
	}
}

在这里插入图片描述
getMethod()方法用于返回sayHello()方法所对应的Method()对象,由于sayHello()方法本身要接受两个参数,因此在使用Class实例的getMethod()方法时,除了要指定方法名称外,也需要指定方法的参数类型。通过Method对象的invoke()方法实现sayHello()方法的调用,并接收sayHello()方法所传入的实参。

内省

JDK中提供了一套API专门用于操作Java对象的属性,它比反射技术操作更加简便,这就是内省。
什么是内省
内省是Java语言对JavaBean类属性、事件和方法的一种标准处理方式,它的出现有利于操作对象属性,并且可以有效的减少代码量
内省访问JavaBean有两种方法
1、先通过java.beans包下的Introspector类获得JavaBean对象的BeanInfo信息,再通过BeanInfo来获取属性的描述器,然后通过这个属性描述器就可以获取某一属性对应的getter和setter方法,最后通过反射机制来调用这些方法。
2、直接通过java.beans包下的PropertyDescriptor类来操作Bean对象。

Person.java

package JavaBean;

class Person{
	private String name;
	private int age;
	
	public String getName(){
		return name;
	}
	
	public void setName(String name){
		this.name=name;
	}
	
	public int getAge(){
		return age;
	}
	
	public void setAge(int age){
		this.age=age;
	}
	
	public String toString(){
		return "姓名:"+this.name+",年龄:"+this.age;
	}
}

Introspector01.java

package JavaBean;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

public class Introspector01{
	public static void main(String args[])throws Exception{
		Person beanObj=new Person();
		BeanInfo bInfoObject=Introspector.getBeanInfo(beanObj.getClass(),beanObj.getClass().getSuperclass());
		String str="内省成员属性:\\n";
		PropertyDescriptor[] mPropertyArray=bInfoObject.getPropertyDescriptors();
		for(int i=0;i<mPropertyArray.length;i++){
			String propertyName=mPropertyArray[i].getName();
			Class propertyType=mPropertyArray[i].getPropertyType();
			str+=propertyName+"("+propertyType.getName()+")\\n";
		}
		System.out.println(str);
	}
}

在这里插入图片描述
修改JavaBean的属性
Introspector02.java

package JavaBean;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

public class Introspector02 {
	public static void main(String[] args)throws Exception{
		Person p=new Person();
		PropertyDescriptor pd=new PropertyDescriptor("name",p.getClass());
		Method methodName=pd.getWriteMethod();
		methodName.invoke(p, "小明");
		String val="20";
		pd=new PropertyDescriptor("age",p.getClass());
		Method methodAge=pd.getWriteMethod();
		Class clazz=pd.getPropertyType();
		if(clazz.equals(int.class)){
			methodAge.invoke(p, Integer.valueOf(val));
		}else{
			methodAge.invoke(p, val);
		}
		System.out.println(p);
	}
}

在这里插入图片描述
需要注意的是,使用内省设置属性时,必须要设置对应数据类型的数据,否则程序会出错。
读取JavaBean的属性
使用PropertyDescriptor类的getWriteMethod()方法就可以获取属性对应的setter方法。在JavaBean中,属性的getter和setter方法是成对出现的,因此Java的内省也提供了读取JavaBean属性的方法,只需要使用PropertyDescriptor类的getReadMethod()方法就可以获取属性对应的getter方法。
Introspector03.java

package JavaBean;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

public class Introspector03 {
	public static void main(String[] args)throws Exception{
		Person p=new Person();
		p.setName("李芳");
		p.setAge(18);
		PropertyDescriptor pd=new PropertyDescriptor("name",p.getClass());
		Method methodName=pd.getReadMethod();
		Object o=methodName.invoke(p);
		System.out.println("姓名:"+o);
		pd=new PropertyDescriptor("age",p.getClass());
		Method methodAge=pd.getReadMethod();
		o=methodAge.invoke(p);
		System.out.println("年龄:"+o);
	}
}

在这里插入图片描述

JSP标签访问JavaBean

为了在JSP页面中简单快捷地访问JavaBean,并且充分地利用JavaBean地特性,JSP规范专门定义了三个JSP标签<jsp:useBean>,<jsp:setProperty>,<jsp:getProperty>。
<jsp:useBean>标签
<jsp:useBean>标签用于在某个指定的域范围中查找一个指定名称的JavaBean对象,如果存在则直接返回该JavaBean对象的引用,如果不存在则实例化一个新的JavaBean对象并将它按指定的名称存储在指定的域范围中。

<jsp:useBean id="beanInstanceName" [scope="{page|request|session|application}"]
{
	class="package.class"|
	type="package.class"|
	class="pageckage.class" type="package.class"|
	beanName="{package.class|<%=expression%>}"type="package.class"
}/>

标签中有5个属性
1、id:用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称。
2、scope:用于指定JavaBean实例对象所存储的域范围,其取值只能是page、request、session和application4个中的一个,其默认值是page
3、type:用于指定JavaBean实例对象的引用变量类型,它必须是JavaBean对象的类名称、父类名称或JavaBean实现的接口名称。type属性的默认值为class属性的设置值,当JSP容器将<jsp:useBean>标签翻译成Servlet程序时,他将使用type属性值作为JavaBean对象引用变量的类型
4、class:用于指定JavaBean的完整类名,JSP容器将使用这个类名来创建JavaBean的实例对象或作为查找到JavaBean对象的类型
5、beanName:用于指定JavaBean的名称,它的值也是a.b.c的形式,这既可以代表一个类的完整名称,也可以代表a/b/c.ser这样的资源文件。beanName属性值将被作为参数传递给java.beans.Beans类的instantiate方法,创建出JavaBean实例对象。需要注意的是,beanName属性值也可以为一个脚本表达式。
在使用<jsp:useBean>标签时,id属性必须指定,scope属性可

以上是关于JavaBean组件的主要内容,如果未能解决你的问题,请参考以下文章

JavaBean简单及使用

JavaBean简单及使用

JavaBean组件

JSP共享javabean

JSP和Servlet[2]

JS+JavaBean判断管理员增删改的操作权限