Java基础加强---day02
五、内省
JavaBean:JavaBean是一种特殊的Java类,主要用于传递数据信息,这种Java类中的方法主要用于访问私有的字段,并且方法名符合某种命名规则。当某个java类中的一些方法符合某种命名规则,那么就可以把它当做JavaBean来使用。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称为值对象(Value Object)。这些信息在类中用私有字段来存储,如果要读取或者设置这些字段的值,那么需要通过一些相应的方法来访问。
去掉前缀(set、get)后,剩下的部分就是属性名,如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改为小写的;如果第二个字母是大写的,第一个字母保留大写。
总之:一个JavaBean类的属性名可以根据方法名推断出来,并且我们根本看不到java类内部的成员变量。
由于JavaBean广泛的应用性,Eclipse有专门的快捷键产生,即getter和setter。
JavaBean的好处:
1、在JavaEE开发中,经常要使用到JavaBean,很多环境要求按照JavaBean,所以JavaBean很重要!
2、JDK中提供了对JavaBean进行操作的一些API,所以操作相对简单方便,这一套API就称为内省。
内省:对应的英文单词为:IntroSpector,有内部视察的意思,主要用于对JavaBean的操作。java提供的API为:PropertyDescriptor。但PropertyDescriptor类对JavaBean的操作过于复杂,也可以通过导入BeanUtils工具包对JavaBean进行内省操作。
万事万物都应该都应该用一个东西来描述,BeanInfo是专门用于描述JavaBean的一个类,也可以通过遍历BeanInfo中所有属性的方式对JavaBean进行描述,IntroSpector.getBeanInfo(<?>class),获取BeanInfo对象。但这种方法操作较为麻烦,可以作为了解。
导入方式:选中工程,右键-->buildPath-->Configure buildPath 然后导入相应的工具包,即可。
产生getter和setter的方式:在类中的空白处,右键-->Source-->Generate Getters and Setters,然后选中相应字段,即可。
抽取方法的方式:选中要抽取成为方法的代码,右键-->refactor-->Extract Method,即可。
下面代码体现:
1、内省的演示:
package itheima.day08;
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;
import com.sun.xml.internal.fastinfoset.sax.Properties;
import itheima.day07.ReflectPoint;
public class IntroSpectorTest {
public static void main(String[] args) throws Exception {
// 提供设置、获取某个字段的类 就是JavaBean
ReflectPoint pt1 = new ReflectPoint(3, 5);
// 获取读取一个字段的方法
String propertyName = "x";
Object returnValue = getProperty(pt1, propertyName);
System.out.println(returnValue);//3
// 设置一个字段的值
Object value =7;
setProperty(pt1,propertyName,value);
// 使用BeanUtils工具包
System.out.println(BeanUtils.getProperty(pt1, "x"));
// BeanUtils工具包的方法更加简便
System.out.println(pt1.getX());//7
// BeanUtils.setProperty(pt1, "birthday.time", "111");//级联操作
// System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));
}
private static void setProperty(ReflectPoint pt1, String propertyName,
Object value) throws Exception {
// PropertyDescriptor(String propertyName, Class<?> beanClass)
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
// 获取set方法
Method methodSetX = pd2.getWriteMethod();
// 方法关联具体的对象
methodSetX.invoke(pt1,value);
}
private static Object getProperty(Object pt1,String propertyName) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
// 操作JavaBean的API,内省
// PropertyDescriptor(String propertyName, Class<?> beanClass)
PropertyDescriptor pd = new PropertyDescriptor(propertyName, pt1.getClass());
// 获取应用于读取字段属性值的方法
Method methodGetX = pd.getReadMethod();
// 关联具体的对象
Object returnValue = methodGetX.invoke(pt1);
// 这是早期的做法
// BeanInfo类专门用于描述JavaBean
BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
// 获取JavaBean中所有的属性值
PropertyDescriptor[] pds =beanInfo.getPropertyDescriptors();
Object retvalue =null;
for(PropertyDescriptor pd1:pds){
// 寻找我们想要的字段名
if(pd1.getName().equals(propertyName)){
Method methodGetx1 = pd.getReadMethod();
retvalue= methodGetx1.invoke(pt1);
}
}
return returnValue;
}
}
六、注解
注解:注解相当于一种标记,在程序中加了注解就等于为程序打上了某种标记;是注解是JDK1.5的一个重要新特性。
JDK中自带有三个注解:
1、@SupressWarning:取消编译器提出的警告,只在源文件时期中有效。
2、@Deprecated:提示元素是过时的,直到运行期都有效。
3、@Override:表示覆盖父类中的方法,只在源文件时期有效,只能用在方法上。
注解相当于程序中要调用的一个类,要在源程序中应用某个注解,得先有这个注解类;好比要调用某个类,必须先开发好这个类。注解的应用结构如下图所示:
@Retention,元注解,用在注解中的注解,有三个取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;分别对应:java源文件、class文件、内存中的字节码。
@Target:指示注释类型所适用的程序元素的种类。如果注释类型声明中不存在 Target 元注释,则声明的类型可以用在任一程序元素上。如果存在这样的元注释,则编译器强制实施指定的使用限制。可以是Filed(字段)、Method(方法)、Type(类型,包括类、接口、注解等)。
注解的属性:注解本身就是给给源程序打上某种标记,则注解的属性就是标记中的标记,即更具体的标记。
下面代码演示:
注解类:
package itheima.day08;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import itheima.day07.EnumTest;
//源注解,指明该注解类的生命周期
@Retention(RetentionPolicy.RUNTIME)
//源注解,指明注解类可以用在什么类型的数据上,即使用目标
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
// 注解的属性
String color() default "bule";
String value();
// 注解的属性有默认值时,应用时可以不必指定
int[] arrayAttr() default {3,2,1};
// 带枚举的注解
EnumTest.TrafficLamp lam() default EnumTest.TrafficLamp.RED;
}
应用注解的类:
package itheima.day08;
//使用"注解类"的类
//在使用注解类时,必须给未初始化的属性赋值
@ItcastAnnotation(color ="red", value = "abc")
public class AnnotationTest {
// 取消警告
@SuppressWarnings("deprecated")
// 只有value一个属性待赋值时,可以省略属性名
@ItcastAnnotation("xyz")
public static void main(String[] args) {
// 过时的方法
System.runFinalizersOnExit(true);
// MyEclipse提示该方法过时
AnnotationTest.sayHello();
// 对使用注解类的类进行反射
if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
// 通过反射的形式获取到注解类
ItcastAnnotation annotation =
(ItcastAnnotation)AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
// @itheima.day08.ItcastAnnotation(color=red, lam=RED, arrayAttr=[3, 2, 1], value=abc)
System.out.println(annotation);
// 获取到注解类之后,可以获取注解类中的属性字段
System.out.println(annotation.color());
System.out.println(annotation.value());
System.out.println(annotation.arrayAttr().length);
System.out.println(annotation.lam().nextLamp().name());
}
}
// 给该方法打上过时的标记,使用的是JDK提供的注解类
@Deprecated
public static void sayHello(){
System.out.println("hello itheima");
}
}
七、泛型
泛型:泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,再调用其add方法即可。
泛型是JDK1.5的一个新特性,没有泛型时,集合中通过Object提供扩展性,但什么类型的对象都可以往同一个集合中放,这就容易引起操作集合中元素时发生一些异常。指定了泛型后,集合中只能放进特定的类型元素,提高了代码的安全性。
泛型的特定术语:
ArrayList<E>:泛型类型;E称为类型变量或者类型参数;
ArrayList<Integer>称为参数化的类型;<>称为typeof;ArrayList称为原始类型。
参数化类型可以引用一个原始类型的对象,编译报告警告;如:
Collection<String>c = new Vector();//兼容JDK1.4以前编写的程序;
原始类型可以引用一个参数化类型的对象,编译报告警告;
Collectionc = new Vector<String>();//原来的方法接受一个集合参数,新的类型也要能传进去。
上限:? extends E。
下限:? super E。
关于泛型,在集合那一章中有详细的介绍,在此不再赘述!
下面代码演示:
package itheima.day08;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
public class GenericTest {
public static void main(String[] args) throws Exception {
// 在泛型出现之前,用Object提供扩展性
// 面向接口的编程
Collection collection1 = new ArrayList();
// 使用Object提供扩展性,弊端很明显:什么东西都可以往集合中放
collection1.add(1);
collection1.add(1.2);
collection1.add("abc");
System.out.println(collection1.size());
// JDK1.5版本以后,用泛型限定存入集合的类型
Collection<String> collection2 = new ArrayList<String>();
collection2.add("abcd");
// 编译通不过,把安全问题扼杀在编译时期
// collection2.add(1.2);
System.out.println(collection2.size());
// 可以使用反射技术透过编译器
Constructor<String> constructor1 =
String.class.getConstructor(StringBuffer.class);
String str2 = constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str2);
ArrayList<Integer> collection3 = new ArrayList<Integer>();
// 同一份字节码,说明字节码与泛型无关,泛型只是给编译器看门的一个道具,仅此而已
System.out.println(collection2.getClass()==collection3.getClass());//true
// 用反射透过编译器
collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc");
System.out.println(collection3.get(0));
}
}