Java帮帮-IT资源分享网
内省:
JavaBean、
Beanutils、注解、泛型
1、内省了解 JavaBean
JavaBean 是一种特殊的 Java 类,主要用于传递数据信息,这种 java 类中的方法主要
用于访问私有的字段,且方法名符合某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个 JavaBean 中,这种
JavaBean 的实例对象通常称之为值对象(Value Object,简称 VO)。这些信息在类中用私有
字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,大家觉得
这些方法的名称叫什么好呢?JavaBean 的属性是根据其中的 setter 和 getter 方法来确定的,
而不是根据其中的成员变量。如果方法名为 setId,中文意思即为设置 id,至于你把它存到
哪个变量上,用管吗?如果方法名为 getId,中文意思即为获取 id,至于你从哪个变量上取,
用管吗?去掉 set 前缀,剩余部分就是属性名,如果剩余部分的第二个字母是小写的,则把
剩余部分的首字母改成小的。
setId()的属性名 id
isLast()的属性名 last
setCPU 的属性名是什么? CPU
getUPS 的属性名是什么? UPS
总之,一个类被当作 javaBean 使用时,JavaBean 的属性是根据方法名推断出来的,它
根本看不到 java 类内部的成员变量。
一个符合 JavaBean 特点的类可以当作普通类一样进行使用,但把它当 JavaBean 用肯定
需要带来一些额外的好处,我们才会去了解和应用 JavaBean!好处如下:
在 Java EE 开发中,经常要使用到 JavaBean。很多环境就要求按 JavaBean 方式进行操
作,别人都这么用和要求这么做,那你就没什么挑选的余地!
JDK 中提供了对 JavaBean 进行操作的一些 API,这套 API 就称为内省。如果要你自己
去通过 getX 方法来访问私有的 x,怎么做,有一定难度吧?用内省这套 api 操作 JavaBean
比用普通类的方式更方便。
2、内省综合案例和 Beanutils 工具包
演示用 eclipse 自动生成 ReflectPoint 类的 setter 和 getter 方法。
直接 new 一个 PropertyDescriptor 对象的方式来让大家了解 JavaBean API 的价值,先用
一段代码读取 JavaBean 的属性,然后再用一段代码设置 JavaBean 的属性。
演示用 eclipse 将读取属性和设置属性的流水帐代码分别抽取成方法:
只要调用这个方法,并给这个方法传递了一个对象、属性名和设置值,它就能完成属性
修改的功能。
得到 BeanInfo 最好采用“obj.getClass()”方式,而不要采用“类名.class”方式,这样程
序更通用。
采用遍历 BeanInfo 的所有属性方式来查找和设置某个 RefectPoint 对象的 x 属性。在程
序中把一个类当作 JavaBean 来看,就是调用 IntroSpector.getBeanInfo 方法,得到的 BeanInfo
对象封装了把这个类当作 JavaBean 看的结果信息。
演示用eclipse如何加入jar包,先只是引入beanutils包,等程序运行出错后再引入logging
包。
在前面内省例子的基础上,用 BeanUtils 类先 get 原来设置好的属性,再将其 set 为一个
新值。
get 属性时返回的结果为字符串,set 属性时可以接受任意类型的对象,通常使用字符串。
用 PropertyUtils 类先 get 原来设置好的属性,再将其 set 为一个新值。
get 属性时返回的结果为该属性本来的类型,set 属性时只接受该属性本来的类型。
演示去掉 JavaBean(ReflectPoint)的 public 修饰符时,BeanUtils 工具包访问 javabean
属性时出现的问题。
Eg:package javaBean.cn.itcast;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.beanutils.BeanUtils;
public class BeansTest {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Person p = new Person();
p.setName("刘昭");
String propertiesName = "name";
String name = extracted(p, propertiesName);//演示了用eclipse
抽取方法
System.out.println(name);
String propertiesAge = "age";
int age = 23;
SetAge(p, propertiesAge, age);
String name1 = BeanUtils.getProperty(p, "name");// 使 用
beanUtils工具包进行获取和设置属性(尽管这些属性是私有的,可是有方法啊,是不是
很方便)
System.out.println(BeanUtils.getProperty(p,
"name").getClass().getName());
System.out.println(name1);
BeanUtils.setProperty(p, "age", 19);
System.out.println(p.getAge());
/*打印结果
* 刘昭
23
java.lang.String
刘昭
19*/
}
private static void SetAge(Person p, String propertiesAge, int age)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor bp1 = new PropertyDescriptor(propertiesAge,
p.getClass());
Method methodSetAge = bp1.getWriteMethod();
methodSetAge.invoke(p,age);
System.out.println(p.getAge());
}
private static String extracted(Object p, String propertiesName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
/*PropertyDescriptor bp = new
PropertyDescriptor(propertiesName, p.getClass());
Method methodGetName = bp.getReadMethod();
Object readVal = methodGetName.invoke(p);
System.out.println(readVal);*/
BeanInfo beanInfo = Introspector.getBeanInfo(p.getClass());
PropertyDescriptor[] pds =
beanInfo.getPropertyDescriptors();
Object retVal = null;
for(PropertyDescriptor pd : pds){
if(pd.getName().equals(propertiesName))
{
Method methodGetX = pd.getReadMethod();
retVal = (String)methodGetX.invoke(p);
break;
}
}
return (String) retVal;
}
}
3、注解(Annotation)
注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记,没加,则等
于没有某种标记。
以后,javac 编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无
何种标记,看你有什么标记,就去干相应的事。
标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
一个注解相当于一个类。
看 java.lang 包,可看到 JDK 中提供的最基本的 annotation。
@SuppressWarning(”deprecation”)--->压制警告
SupressWarning 是告知编译器或开发工具等提示指定的编译器警告;
“deprecation”是告知具体的信息即方法已过时。
@Deprecated(过时的方法,对于不再使用的方法,可能别人或别的地方有调用这个方法,
不能删除完事)
直接在刚才的类中增加一个方法,并加上@Deprecated 标注,在另外一个类中调用这个方法。
测试一下。
@SuppressWarnings("deprecation") (用这个可以告诉 程序说,我知道调用的方法过时了)
@Override--->提示覆盖(父类方法)
public boolean equals(Reflect other)方法与 HashSet 结合讲解
像 person 类,覆盖父类的 equals 和 hashCode 方法,人家接收的参数是 Object,人们习
惯总是传入自己的对象,造成覆盖失败,变成重载!
演示和讲解@Target 元注解
Target(告诉编译器,自定义的注解类可以用在方法还是类 ....),设置 Target 等于
ElementType.METHOD , 原 来 加 在 类 上 的 注 解 就 报 错 了 , 改 为 用 数 组 方 式 设 置
{ElementType.METHOD,ElementType.TYPE}就可以了。
元注解以及其枚举属性值不用记,只要会看 jdk 提供那几个基本注解的 API 帮助文档的定义
或其源代码,按图索骥即可查到,或者直接看 java.lang.annotation 包下面的类。
(自定义注解)示例代码:
@Retention(RetentionPolicy.RUNTIME)//告诉程序说,这个注解要保存到运行时期
@Target({ElementType.METHOD,ElementType.TYPE})//告诉编译器,这个注解可以用在方法
上,也可以用在类上
public @interface MyAnnotation {
String color() default "yellow";//默认缺省值为yellow
String value() ;//不指定
int [] arrayAttr() default {1,2};//默认为{1,2}
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;//
枚举类
MetaAnnotation annotationAttr() default
@MetaAnnotation("xxx");//属性中加注解,用@。可以在对别的类加注解时,改变值
}
为注解增加基本属性
(可以是八种基本数据类型,String ,数组,枚举,注解,Class)
什么是注解的属性?
一个注解相当于一个胸牌,如果你胸前贴了胸牌,就是传智播客的学生,否则,就不是。
如果还想区分出是传智播客哪个班的学生,这时候可以为胸牌在增加一个属性来进行区分。
加了属性的标记效果为:@MyAnnotation(color="red")
定义基本类型的属性和应用属性:
在注解类中增加 String color();
@MyAnnotation(color="red")
用反射方式获得注解对应的实例对象后,再通过该对象调用属性对应的方法
MyAnnotation a = (MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class);
System.out.println(a.color());
可以认为上面这个@MyAnnotation 是 MyAnnotaion 类的一个实例对象
如果注解中有一个名称为 value 的属性,且你只想设置 value 属性(即其他属性都采用默认
值或者你只有一个 value 属性),那么可以省略 value=部分,例如:@MyAnnotation("lhm")。
枚举和注解都是特殊的类,不能用 new 创建它们的实例对象,创建枚举的实例对象就是在
其中增加元素。
在程序中如何创建出一个注解的实例对象啊?直接用@放上一个标记即可
Eg:package july78javaEnhance;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)// 元 注 解 : 信 息 的 信 息 就 是 元 信 息
RUNTIME,保留到运行期间
/**
* 指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,
* 则保留策略默认为 RetentionPolicy.CLASS。
*/
//@Target(ElementType.METHOD)如果加上这句的话(只能作用于方法),那边就会报
错!
public @interface AnnotationDemo12 {//注解
String color() default "blue";//相当于构造方法一样,如果给了它默认的初
值,可以不用再设置(有默认的)
String value();
int []age();//数组和枚举类型的注解
//还有注解类型的注解,暂时没学会
MetaAnnotation annotation();
//上面的MetaAnnotation是自己定义的一个注解类型,这样的话对于应用了当前注
解的AnnotationDemo11类,就必须写上注解类型的属性
}
package july78javaEnhance;
import java.util.Arrays;
//备注:应用注解后,它的属性你你没有写上,它会给予提醒!missing attribution
@AnnotationDemo12(color = "red",value = "j2ee",age = {1,2,3},
annotation = @MetaAnnotation(sex = "男"))//注解加注解
public class AnnotationDemo11 {
@SuppressWarnings("deprecation")//压缩注解,一个注解就是一个类,用到
的一个注解就相当于是调用的实例对象
@AnnotationDemo12(value = "jase",age = {1,2,3}, annotation =
@MetaAnnotation(sex = "女"))//备注:如果别的属性有默认值,只有一个属性需要
你设置,那么你就不需要写上全部的
//赋值表达式,如上直接写上"jase"就行
public static void main(String[] args) {
System.runFinalizersOnExit(true);//表示已经过时的方法,开发工具会
给它中间加上一天横线
/**
* boolean isAnnotationPresent(Class extends Annotation>
annotationClass)
如果指定类型的注释存在于此元素上,则返回 true,否则返回 false。
*/
if(AnnotationDemo11.class.isAnnotationPresent(AnnotationDemo12.
class)){
AnnotationDemo12 annocation =
(AnnotationDemo12)AnnotationDemo11.class
.getAnnotation(AnnotationDemo12.class);// 证 明 这 里
面有你的注解
System.out.println(annocation.color());//调用属性
System.out.println(Arrays.toString(annocation.age()));// 将 数 组
打印出来
System.out.println(annocation.annotation().sex());// 相当
于调用属性的属性
}
}
}
4、泛型
Jdk 1.5 以前的集合类中存在什么问题
ArrayList collection = new ArrayList();
collection.add(1);
collection.add(1L);
collection.add("abc");
int i = (Integer) collection.get(1);//编译要强制类型转换且运行时出错!
Jdk 1.5 的集合类希望你在定义集合时,明确表示你要向集合中装哪种类型的数据,无法加
入指定类型以外的数据
ArrayList
collection2.add(1);
/*collection2.add(1L);
collection2.add(“abc”);*///这两行代码编译时就报告了语法错误
int i2 = collection2.get(0);//不需要再进行类型转换
泛型是提供给 javac 编译器使用的,
利用反射穿透泛型限制 (暴力反射)
泛型能绝对保证集合中存入数据都是它限定的类型吗?先看下边的代码
package july78javaEnhance;
import java.util.ArrayList;
public class Demo23 {
public static void main(String[] args) {
ArrayList
System.out.println(collection1.getClass()==collection2.getClas
s());
collection2.add(“真暴力”);//这句会报错
collection2.getClass().getMethod("add",
Object.class).invoke(collection2, "真暴力");
System.out.println(collection2.get(0)); //结果却为真暴力
//已经限制集合中元素的类型为Integer,可用反射却能将String存入,为什
么? 这是因为泛型是给编译器用的,运行时就没有这些泛型信息了,这叫做“去泛型化”,
所以可以通过反射,获取集合字节码加入非指定的类型。
}
}
泛型中的?通配符的扩展
限定通配符的上边界:
正确:Vector extends Number> x = new Vector
错误:Vector extends Number> x = new Vector
限定通配符的下边界:
正确:Vector super Integer> x = new Vector
错误:Vector super Integer> x = new Vector
提示:
限定通配符总是包括自己。
?只能用作引用,不能用它去给其他变量赋值
Vector extends Number> y = new Vector
Vector
上面的代码错误,原理与Vector
只能通过强制类型转换方式来赋值。
Eg:
泛型集合类的综合案例
能写出下面的代码即代表掌握了Java的泛型集合类:
HashMap
hm.put("zxx",19);
hm.put("lis",18);
Set
for(Map.Entry
System.out.println(me.getKey() + ":" + me.getValue());
}
由C++的模板函数引入自定义泛型
如下函数的结构很相似,仅类型不同:
int add(int x,int y) {
return x+y;
}
float add(float x,float y) {
return x+y;
}
double add(double x,double y) {
return x+y;
}
C++用模板函数解决,只写一个通用的方法,它可以适应各种类型,示意代码如下:
template
T add(T x,T y) {
return (T) (x+y);
}
类型参数的类型推断(花了张老师两天的时间总结)
l 编译器判断范型方法的实际类型参数的过程称为类型推断,类型推断是相对于知觉推断
的,其实现方法是一种非常复杂的过程。
l 根据调用泛型方法时实际传递的参数类型或返回值的类型来推断,具体规则如下:
1.当某个类型变量只在整个参数列表中的所有参数和返回值中的一处被应用了,那么根据
调用方法时该处的实际应用类型来确定,这很容易凭着感觉推断出来,即直接根据调用方法
时传递的参数类型或返回值来决定泛型参数的类型,例如:
swap(new String[3],3,4) à static
2.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方
法时这多处的实际应用类型都对应同一种类型来确定,这很容易凭着感觉推断出来,例如:
add(3,5) à static
3.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方
法时这多处的实际应用类型对应到了不同的类型,且没有使用返回值,这时候取多个参数中
的最大交集类型,例如,下面语句实际对应的类型就是 Number 了,编译没问题,只是运行
时出问题:
fill(new Integer[3],3.5f) à static
4.当某个类型变量在整个参数列表中的所有参数和返回值中的多处被应用了,如果调用方
法时这多处的实际应用类型对应到了不同的类型, 并且使用返回值,这时候优先考虑返回
值的类型,例如,下面语句实际对应的类型就是 Integer 了,编译将报告错误,将变量 x 的
类型改为 float,对比 eclipse 报告的错误提示,接着再将变量 x 类型改为 Number,则没有了
错误:
int x =(3,3.5f) à static
5.参数类型的类型推断具有传递性,下面第一种情况推断实际参数类型为 Object,编译没
有问题,而第二种情况则根据参数化的 Vector 类实例将类型变量直接确定为 String 类型,编
译将出现问题:
copy(new Integer[5],new String[5]) à static
copy(new Vector
定义泛型类型
如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保
持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型,语
法格式如下:
public class GenericDao
private T field1;
public void save(T obj){}
public T getById(int id){}
}
类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两
种方式都可以:
GenericDao
new genericDao
注意:
在对泛型类型进行参数化时,类型参数的实例必须是引用类型,不能是基本类型。
当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静
态方法调用。因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的
类型参数。
问题:类中只有一个方法需要使用泛型,是使用类级别的泛型,还是使用方法级别的泛
型?
(Create)、查询(Retrieve)(重新得到数据)、更新(Update)和删除(Delete)几个单词的首字母简
写
import java.util.Set;
//dao data access object(数据访问对象)--->crud
public class GenericDao
public void add(E x){
}
public E findById(int id){
return null;
}
public void delete(E obj){
}
public void delete(int id){
}
public void update(E obj){
}
public static
}
public E findByUserName(String name){
return null;
}
public Set
return null;
}
}
Java帮帮-IT资源分享网
|