您的位置:首页 > 职场人生

黑马程序员_java高新技术(2)

2013-04-21 00:56 330 查看
------- android培训java培训、期待与您交流! ----------

内省

主要用于对javaBean进行操作。

Javabean是一个特殊的java类,而这个java类中的方法符合某种约定的规则。

比如类中有一个属性private int x。

那么它的读取方法就是

public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}


这就是javabean的规范定义的属性读取方式。

当把这个类当做javabean使用的时候,外界就是通过看这个setX中的后边这个X

来有javabean的X属性,而不是java类的属性X。

这个属性的名字是通过方法名推断出来的。

要分清javabean的属性和java类的属性的由来。

在分析其javabean中定义的属性的时候

如果方法中的X第二个字母是小的,则把第一个字母变成小的

比如getTime--->属性就是time gettime---->属性是time

但是 getTIME->属性名是TIME。

使用内省的方式对javabean进行操作

为什么我们要将一个类当成javabean去处理

肯定是当做javabean的时候,能有一些方便。

通过api就可以简单的处理javabean

Javabean的作用,比如说在struts2框架中要两个模块之间传递多个信息。

这就就是这个框架为什么能从一个表单数据直接创建一个对象并放到Action中的

原因。其中一定是核心的控制器org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter从表单中读取了数据,它通过api对这个javabean使用简单的方式进行操作,然后将这个javabean传递给了对应的Action。。十分重要。

用内省去操作javabean

class Point
{
private int x;
public Point(int x) {
super();
this.x = x;
}
public int getX() { return x; } public void setX(int x) { this.x = x; }}

Point point = new Point(1);
String propertyName = "x";
//想要获得javabean中x属性的值,而不是x的成员变量。
//首先要把x变成一个指定的格式然后加getX,然后再用反射得到这个Method
//这个过程很复杂,而也正是内省的出现的原因。
//api定义了一个PropertyDescriptor去操作javabean的属性。
PropertyDescriptor pd = new PropertyDescriptor(propertyName,point.getClass());
//这个pd就代表那个javabean的属性。从而得到了javabean属性的读写方法。
Object retVal = pd.getReadMethod().invoke(point);//没有参数的仅仅写上对象名就可以。
System.out.println(retVal);
pd.getWriteMethod().invoke(point,2);
Object retVal1 = pd.getReadMethod().invoke(point);
System.out.println(retVal1);


获得其值得时候,还是反射的那一套,但是省略了对其方法获取的时候的名字规范问题。得到一个get和set方法很简单。

这里提供一个比较老的方式,也是比较复杂的方式

BeanInfo beanInfo = Introspector.getBeanInfo(point.getClass());

PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();

for(PropertyDescriptor pd:pds)
{
if(pd.getName().equals(propertyName))
{
Method method = pd.getReadMethod();
method.invoke(point);
break;
}
}


这就是内省对javabean的读取操作

由于对javabean的操作比较广泛。

所有有一些人就封装了一些专门对javabean操作的类,更加的方便了对javabean的书写。

BeanUtils去设置和读取javabean的属性。(看看有多么的方便)

一定要记住,用BeanUtils去操作javabean的时候,这个javabean一定要是public

直接使用BeanUtils.getProperty(point,”x”);

就可以得到这个x属性的值了。

使用BeanUtils.setProperty(point,”x”,”9”);

但是使用这种方式,无论哪种类型,都返回一个String类型。

在设置的时候,都是使用String类型设置进去的。

它为什么要这样做呢?在web中填写的数值传给服务器的是字符串。

就直接可以使用这个类中的方法了。而避免了我们在程序中去人为的去转换。

还有一种方式,使用

PropertyUtils.setProperty(point,propertyName,123);
System.out.println(PropertyUtils.getProperty(point,"x"));

这里传入的参数必须是指定的类型,而不都是String

注解:十分的重要(JDK1.5新特性)

注解也就是告诉开发工具或者告诉编译器说你应该怎么,不应该怎么。

传递某种信息的一个标记。

一个注解就是一个类,使用一个注解就相当于创建了一个实例对象。

比如说

@Deprecated //这个注解就是说下边这个方法已经过时了,不适合使用了。
public static void sayHello()
{
System.out.println("nihao");
}


在覆盖一个父类的方法的时候,

@Override //也就是说下边这个方法是覆盖子类。会检查是否满足覆盖的条件。

public boolean equals(Object o)

{

return true;

}

总结:注解相当于一种标记,加了注解就等于为程序打上某种标记。有什么标记就去采取相应的动作。

一个注解有下列三部分。



一个注解的生命周期有三个阶段:

Java源文件—>class文件--->内存中的字节码。

默认的生命周期情况是在class文件阶段。也就是无法在运行的时候检验一个类上是否有注解。

@Override是在源文件时候看的

@suppressWarnings在源文件时候看的

@Deprecated是在运行的时候看的。

首先我们定义了一个注解类

package day11;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//元注解,也就是注解的注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})//也就是说这个注解只能放在方法上。
public @interface ItcastAnnotation
{

}


然后定义使用这个注解的类

package day11;
@ItcastAnnotation
public class AnnotationDemo {
@ItcastAnnotation
public static void main(String[] args)
{
if(AnnotationDemo.class.isAnnotationPresent(ItcastAnnotation.class))
//去检查一个 类上边是否有某个注解
{
ItcastAnnotation it = AnnotationDemo.class.getAnnotation(ItcastAnnotation.class);
//如果有这个注解,就得到这个注解。
}
}
}
package day17;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
/*
* 注释类型 Retention
指示注释类型的注释要保留多久。如果注释类型声明中不存在 Retention 注释,则保留策略默认为 RetentionPolicy.CLASS.
*
*
* CLASS
编译器将把注释记录在类文件中,但在运行时 VM 不需要保留注释。 将.class(不是存字节码)调入到内存中的时候也有转换(去掉了class注解)。转换成字节码文件对象(才是字节码)。
RUNTIME
编译器将把注释记录在类文件中,在运行时 VM 将保留注释,因此可以反射性地读取。(在字节码中也有注释)
SOURCE
编译器要丢弃的注释 ,也就是编译的时候就去掉这个注解了。

* */
@Target(ElementType.METHOD)
/*
* 指示注释类型所适用的程序元素的种类。
* 如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。
* 如果存在这样的元注释,则编译器强制实施指定的使用限制。
*
* ElementType
*
* 枚举常量摘要
ANNOTATION_TYPE
注释类型声明
CONSTRUCTOR
构造方法声明
FIELD
字段声明(包括枚举常量)
LOCAL_VARIABLE
局部变量声明
METHOD
方法声明
PACKAGE
包声明
PARAMETER
参数声明
TYPE
类、接口(包括注释类型)或枚举声明

* 这些常量与 Target 元注释类型一起使用,以指定在什么情况下使用注释类型是合法的
*
* */
public @interface MyAnnotation
{
}


为注解增加属性。注解的功能之所以强大,是因为它具有属性。

其实注解很像接口,其实他的属性就很像方法了。

比如说有一个属性叫color,这个color方法返回值是String

这个方法是abstract的并且是public的。与接口差不多。

public @interface ItcastAnnotation
{
String color();
}

在使用的时候,也就是认为你这个注解有一个属性。

设置值得时候是类似于属性的形式用

@ItcastAnnotation(color="red")//给这个注解的属性赋值。也就是给一个String类型的属性值放入了一个red值。

有了这个属性值,现在打印这个注解对象。

if(AnnotationDemo.class.isAnnotationPresent(ItcastAnnotation.class))

{

ItcastAnnotation it =AnnotationDemo.class.getAnnotation(ItcastAnnotation.class);

System.out.println(it.color());

//取值的时候还是用方法的形式去调用,因为它相当于一个类,也就是调用一个方法。

}

输出 red

但是为什么设置值的时候有些属性是这样用,比如

@SuppressWarnings("deprecation")。

这里的"deprecation"也是一个属性,只是这个属性的名字非常的特殊。特殊的可以把名字和=号省略。

例如仅仅只有一个value属性的时候,

public@interface ItcastAnnotation

{

String value();

}

赋值的时候就可以这样写

@ItcastAnnotation("able")

得到的时候

ItcastAnnotation it = AnnotationDemo.class.getAnnotation(ItcastAnnotation.class);
System.out.println(it.value());

输出able

特殊的属性:

1.数组类型的属性

public@interface ItcastAnnotation

{

int[] arrayAttr();

}

设置值的时候

@ItcastAnnotation(arrayAttr = {1,2,3,4})

使用的时候

System.out.println(Arrays.toString(it.arrayAttr()));

输出:[1, 2, 3, 4]。

2.枚举类型的属性。

定义一个内部枚举类

public class EnumTest {
public enum TrafficLamp
{
RED(1)
{
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return GREEN;
}
},

GREEN(2)
{
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return YELLOW;
}

},
YELLOW(3)
{
@Override
public TrafficLamp nextLamp() {
// TODO Auto-generated method stub
return RED;
}
}
;
private TrafficLamp()
{}
private TrafficLamp(int time)
{
this.time = time;

}
public abstract TrafficLamp nextLamp();
private int time;
}

}

定义一个注释

public@interface ItcastAnnotation

{

EnumTest.TrafficLamp lamp();

}

去设置它的值

@ItcastAnnotation(lamp=EnumTest.TrafficLamp.GREEN)

然后去调用

System.out.println(it.lamp().nextLamp());

输出 YELLOW

3.注解类型的属性(注解嵌套注解)

public@interface ItcastAnnotation

{

MetaAnnotation annotationAttr();

}

而MetaAnnotation也是一个注解

public@interface MetaAnnotation {

String value();

}

所以给它赋值的时候使用

@ItcastAnnotation(annotationAttr=@MetaAnnotation("wangdabin"))

@MetaAnnotation("wangdabin")表示一个MetaAnnotation的实例,参数表示这个注解的value属性。

得到这个属性的值

System.out.println(it.annotationAttr().value());

也就是“wangdabin”

泛型的复习:

在没有使用泛型的时候,只要有对象,不管什么类型的对象,都可以存储进同一个集合中,使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象,这样更安全,并且当从集合获取一个对象时候,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。

泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,但是编译器编译完成之后,会去掉类型的信息,使程序运行效率不受影响。编译完成后就没有这些泛型类型了。Jvm根本不知道这些集合中到底存放的是哪种元素,仅仅是java编译器知道。这叫“去类型化”。

ArrayList<String>al = new ArrayList<String>();

ArrayList<Integer>all = new ArrayList<Integer>();

System.out.println(al.getClass()==all.getClass());

这也就是为什么al.getClass==all.getClass为true的原因了。

使用反射仅仅是给编译器看的,用反射可以穿过泛型,给它加不同类型的值。

ArrayList<E> 泛型类型

E称为类型变量或者类型参数

ArrayList<Integer>称为参数化类型

Integer称为类型参数的实例或实际类型参数。

<>念 typeof

ArrayList 为原始类型。

兼容性:(可不可以不就是编译器的一句话吗?)

参数化类型可以引用一个原始类型的对象。

原始类型可以引用一个参数化类型的对象。

参数化类型不考虑类型参数的继承关系。

在创建数组实例的时候,数组元素不能使用参数化的类型。

泛型中的?通配符。

假定一个函数要打印一个任意类型的集合

ArrayList<Integer>al = new ArrayList<Integer>();

printCollection(al);

public static voidprintCollection(Collection<Object> collection)

{

}

由于一个Collection<Object>仅仅只能传递一个Collection<Object>的参数

所以一个ArrayList<Integer>的类型是不能传给这个参数的。不要考虑父子关系。

在jdk1.5中提供了一种统配符的方式。去解决这种方式。

public staticvoid printCollection(Collection<?> collection)

{

}

就表示我这个问号可以指向任意的类型。不管什么类型传给它,它都接收。

表示任意类型。

注意:一个凡是含有统配符作为一个引用变量去引用一个类型,但是不能拿着这个变量去调用一个与参数有关系的方法。

也就是不能调用collection.add();

可以调用这个collection.size()这个方法。

使用?通配符可以引用其他各种参数化的类型,?统配符定义的变量主要用作引用

可以调用与参数无关的方法,不能调用与参数化有关的方法。

切记:一个通配符仅仅作为一个引用变量去引用一个类型,出现在函数定义的参数中或者一个变量的引用类型中或者函数返回值得地方。

统配符的扩展。

限定通配符的上边界:

例如: ArrayList<? Extends Number> al;

也就表示了al可以引用Number或者Number的子类的集合

那个类型继承了Numebr

限定通配符的下边界:

ArrayList<? Super Integer> al ;

也就表示了al可以引用Integer或者integer的父类的集合。

当返回一个<?>类型的值时候,只能用一个 ArrayList<?>的引用去引用它

也就是一个ArrayList<?>的参数可以接收存储任意元素的类型,

但是一个返回值为ArrayList<?>的参数只能用ArrayList<?>去接收。

十分的重要

只有引用类型才能作为泛型方法的实际参数。

定义一个基于泛型的方法

//这里的<T>定义了一个类型

//申明了一个新类型,这个类型是T

publicstatic <T> T add(T x,T y)

{

return x;

}

这个T的类型会根据你调用这个函数的参数去决定这个类型是什么。

比如:add(3,5),这里的T就是integer类型,返回的也是integer

但是add(3.5,3),这里的T就是Number,返回的也是number

Add(3,“sfd”),这里的T就是Object类型,返回的也是Object

但是对于T(泛型)的实际类型来说,只能接受引用类型,而不能是基本类型。

除了在应用泛型的时候可以使用extends限定符的时候,比如ArrayList<? Extends Number> al;

在定义泛型的时候(而不像上边使用它的时候)也可以使用extends限定符,例如

public static <T extends Number> T add(T x,T y){return x;}

也就是定义了一个泛型,这个类型必须是Number的子类。

这时候,add(“1”,”2”);就不对了。

当定义一个

privatestatic <T> T autoConvert(Object obj)autoConvert(Object obj)

{

return (T)obj;

}

Object o = “abc”;

String s =autoConvert(obj)

而这个类型T就是根据定义了什么类型,就返回什么(这里是String)

所以T的类型就是String

其实定义一个泛型方法,就也是在返回值类型之前用<T>来说明这个类型。

这个类型是什么由传递的参数或者由其返回的那个定义的实际类型去决定。

定义一个泛型类。

是为了对类中的多个方法进行同时限定

比如

Class Dao

{

Public <T>voidadd(T x)

{

}

Public <T>T findByid(int id)

{

Return null;

}

}

当我想限定其中俩个T都必须指向同一个类型的时候,使用定义泛型函数就没有办法了。这时候,就的使用泛型类做一个整体的限定了。

classPersonDao<T>

{

public void add(T t)

{

}

public T findById()

{

return null;

}

}

这时候对增加和寻找就必须是同一类型的对象。

注意。在定义的一个泛型类中,一个静态方法不能含有泛型的那个类型的。

如果是一个静态方法也需要泛型,那么你单独玩吧,也就是自己定义一个泛型方法,和泛型类没有关系。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: