Java之反射、枚举、内省、注解
2014-09-02 00:49
465 查看
一:反射技术:
1.概述:其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象, 并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个 类进行解剖,解剖后的属性、方法都为对象
2.反射的好处:大大的增强了程序的扩展性。
3.反射的基本步骤:
3.1.获得Class对象,就是获取到指定的名称的字节码文件对象。
3.2.实例化对象,获得类的属性、方法或构造函数。
3.3.访问属性、调用方法、调用构造函数创建对象。
4.获取这个Class对象,有三种方式:
4.1.通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。
4.2.每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
4.3.使用的Class类中的方法,静态的forName方法。
指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。4.3.1. 根据给定的类名来获得 用于类加载
String classname ="cn.itcast.reflect.Person";// 来自配置文件
Class clazz = Class.forName(classname);// 此对象代表Person.class4.3.2.如果拿到了对象,不知道是什么类型 用于获得对象的类型
Object obj = new Person();Class clazz1 = obj.getClass();// 获得对象具体的类型
4.3.3.如果是明确地获得某个类的Class对象 主要用于传参
Class clazz2 = Person.class;5.反射的用法:
5.1.需要获得java类的各个组成部分,首先需要获得类的Class对象,获得Class对象的三种方式:
Class.forName(classname) 用于做类加载obj.getClass() 用于获得对象的类型
类名.class 用于获得指定的类型,传参用
5.2.反射类的成员方法:
Classclazz = Person.class;Methodmethod = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
5.3.反射类的构造函数:
Constructorcon = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})con.newInstance(params...)
5.4.反射类的属性:
Fieldfield = clazz.getField(fieldName);field.setAccessible(true);
field.setObject(value);
6.获取了字节码文件对象后,最终都需要创建指定类的对象:
创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):6.1.调用空参数的构造函数:使用了Class类中的newInstance()方法。
6.2.调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的 newInstance(实际参数)进行对象的初始化。
代码示例如下:1.定义被反射的类:
package com.JavaSE.Reflect; public class Point { public int x; private int y; public String str1 = "ball"; public String str2 = "basketball"; public String str3 = "itcast"; public static String str4 = "ball"; public Point(int x, int y) { super(); this.x = x; this.y = y; } }
2.反射实例:
package com.JavaSE.Reflect; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Reflect { public static void main(String[] args) throws Exception { int i = 0; Object obj = i;//单独的基本数据类型赋给Object会被自动装箱 int[] in = new int[2]; //Object[] object = in;//会报错,因为数组in中存放的是int基本类型数据,而基本数据类型是没有父类的 //因此不能转换为Object System.out.println(obj.getClass());//由于基本数据的自动装箱,obj为Integer类型 Class clazz = int.class; System.out.println(clazz.getSuperclass());//基本数据类型的父类为null // 反射构造方法 Constructor[] constructors = String.class.getConstructors(); Constructor<String> constructor = String.class .getConstructor(StringBuffer.class);// 通过类的字节码的方法获取一个构造方法的对象 String str = (String) constructor.newInstance(new StringBuffer("abc"));// 通过该对象实例化该类 System.out.println(str.charAt(2)); // 反射属性字段 Point p1 = new Point(3, 5); Field fld1 = p1.getClass().getField("x");// 将x属性属性对象关联起来 System.out.println(fld1.get(p1));// 要获取属性值,必须通过传递被映射的对象 Field fld2 = p1.getClass().getDeclaredField("y");// 映射所有申明过的属性(public,<span style="font-family: Arial; ">private,</span><span style="font-family: Arial; ">protected</span><span style="font-family: Arial; "> </span> fld2.setAccessible(true);// 通过暴力反射获取私有属性 System.out.println(fld2.get(p1)); changeStringValue(p1);// 通过反射修改成员变量的值 System.out.println(p1.str1 + ":" + p1.str2 + ":" + p1.str3 + ":"+ p1.str4); // 成员方法的反射 String str1 = "abc"; Method method = str1.getClass().getMethod("charAt", int.class);// 将方法与方法对象关联 System.out.println(method.invoke(str1, 1));// 根据该方法的对象调用该方法(参数是方法的对象和方法参数),若第一个参 //数为Null则调用的是静态方法 System.out.println(method.invoke(str1, new Object[] { 2 }));// 将参数自动转化 // 对main方法进行反射和调用 String className = args[0];// 假设main方法所在的类名为className Method methodMain = Class.forName(className).getMethod("main", String[].class);// 将方法和方法对象相关联 methodMain.invoke(null, (Object) new String[] { "111", "222", "333" });// 由于在Java1.4以后main方法中会将new String //中的数组对象拆开(编译器特殊处理), // 因此就有了三个对象,造成参数不匹配,解决办 //法就是在前面加一个(Object)把他看成是一个 //对象 // 对数组的反射 String[] str2 = new String[] { "444", "555", "666" }; printObject(str2); printObject(new int[] { 1, 2, 3 }); } // 对数组的反射 private static void printObject(Object obj) { Class clazz = obj.getClass(); if (clazz.isArray()) { for (int i = 0; i < Array.getLength(obj); i++) { System.out.println(Array.get(obj, i)); } } else { System.out.println(obj); } } // 通过反射修改成员变量的值 private static void changeStringValue(Object p2) throws Exception { Field[] fields = p2.getClass().getDeclaredFields();// 获取所有属性 for (Field field : fields) { if (field.getType() == String.class) {// 当属性类型为String时进行修改 String oldValue = (String) field.get(p2); String newValue = oldValue.replace('b', 'a');// 替换字符 field.set(p2, newValue);// 将替换的字符重新放 } } } } class TestMain { public static void main(String[] args) { for (String arg : args) { System.out.println(arg); } } }
二:枚举:
1.概述:枚举就是一个特殊的java类,可以定义属性、方法、构造函数、实现接口、继承类,对象的某个属性的值不能是
任意的,必须为固定的一组取值其中的某一个,因此出现了枚举。
2.直接来代码最合适:
代码如下:1.定义枚举
package com.JavaSE.Enum; //枚举 public enum WeekDayEnum { Sun,Mon,Tue,Wen,Thr,Fri,Sat; }
2.枚举的原理
package com.JavaSE.Enum; /* * 枚举 * 1.就是一个类,在该类中每一个成员变量都是一个final static变量 * 2.有一个私有构造函数,只能通过自己的内部的函数来实例化对象,因此只能实例化已经定义好的对象 * 3.匿名内部类其实就是一个子类,该子类实现父类的方法 */ //利用类来解释枚举内部原理 public abstract class WeekDayClass { private WeekDayClass() { } public final static WeekDayClass Sun = new WeekDayClass(){//匿名内部类 @Override public WeekDayClass nextDay() {//重写nextDay方法 return Mon; } }; public final static WeekDayClass Mon = new WeekDayClass(){ @Override public WeekDayClass nextDay() { // TODO Auto-generated method stub return Tue; } }; public final static WeekDayClass Tue = new WeekDayClass(){ @Override public WeekDayClass nextDay() { // TODO Auto-generated method stub return Sun; } }; public abstract WeekDayClass nextDay(); // public WeekDay nextDay(){ // if(this == Sun){ // return Mon; // }else if(this == Mon){ // return Tue; // }else{ // return Sun; // } // } public String toString(){ if(this == Sun){ return "Sun"; }else if(this == Mon){ return "Mon"; }else{ return "Tue"; } } }
3.枚举的加强
package com.JavaSE.Enum; /* * 1.在枚举中可以有构造函数(代参数或则不代参数),变量根据是否带参数来实例化 * 2.单只有一个变量时则为单例模式 */ public enum EnumEnhance { GREEN(10){ @Override public EnumEnhance next() { // TODO Auto-generated method stub return RED; } }, RED{ @Override public EnumEnhance next() { // TODO Auto-generated method stub return YELLOW; } }, YELLOW{ @Override public EnumEnhance next() { // TODO Auto-generated method stub return GREEN; } }; public abstract EnumEnhance next(); private EnumEnhance() {//枚举中的方法必须在变量之后 System.out.println("first"); } private EnumEnhance(int days){//带有参数的都早函数 System.out.println("second"); } public static EnumEnhance GREEN(int i) {//静态枚举构造函数,访问时为EnumEnhance.GREEN(10) // TODO Auto-generated method stub System.out.println("three"); return null; } }
4.枚举的应用
package com.JavaSE.Enum; public class Test { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub //利用模拟枚举的类实现枚举的功能 // WeekDayClass weekday1 = WeekDayClass.Sun; // WeekDayClass weekday2 = WeekDayClass.Mon; // WeekDayClass weekday3 = WeekDayClass.Tue; // System.out.println(weekday1.toString()); // System.out.println(weekday2.toString()); // System.out.println(weekday3.toString()); // // System.out.println(weekday1.nextDay()); // System.out.println(weekday2.nextDay()); // System.out.println(weekday3.nextDay()); // //枚举实例 // WeekDayEnum weekdayEnum = WeekDayEnum.Mon; // System.out.println(weekdayEnum); // System.out.println(weekdayEnum.name()); // System.out.println(weekdayEnum.ordinal()); // System.out.println(weekdayEnum.valueOf("Tue")); // System.out.println(weekdayEnum.values().length); //枚举加强 EnumEnhance RED = EnumEnhance.RED; System.out.println(RED.next()); } }
三:内省:即对JavaBean的操作
1.说明:对象JavaBean的简单内省操作(简单方便:PropertyDescriptor)和复 杂操作(稍微复杂点:IntroSpector)
2.步奏:
2. 1.创建PropertyDescriptor对象
2.2.获取JavaBean的方法对象
2.3.通过方法对象获取属性值
2.4.修改属性值
代码如下:1.JavaBean:
package com.JavaSE.IntroSpector; public class Point { private int x; private int y; public Point(){ } public Point(int x, int y) { super(); this.x = x; this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
2.对JavaBean的操作
package com.JavaSE.IntroSpector; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; //对象JavaBean的简单内省操作(简单方便:PropertyDescriptor)和复杂操作(稍微复杂点:IntroSpector) //通过内省来操纵JavaBean,通过PropertyDescriptor类才操作,调用该类时也是用的反射 public class IntroSpectorTest { /** * 1.创建PropertyDescriptor对象 * 2.获取JavaBean的方法对象 * 3.通过方法对象获取属性值 * 4.修改属性值 * @param args * @throws Exception */ public static void main(String[] args) throws Exception { Point p1 = new Point(3,5);//JavaBean Point //获取JavaBean的属性 Object value = getProperties(p1); System.out.println(value); //设置JavaBean的属性(简单方式:PropertyDescriptor) Object y = 7;//所修改属性的值 setPropertiesEasy(p1, y); System.out.println(p1.getY()); //设置JavaBean的属性(复杂方式:IntroSpector) setPropertiesComplex(p1,y); System.out.println(p1.getY()); } private static Object getProperties(Point p1) throws Exception{ //1.创建PropertyDescriptor对象,通过属性名和类的字节码初始化一个PropertyDescriptor对象 PropertyDescriptor pd = new PropertyDescriptor("x", p1.getClass()); //2.获取JavaBean的方法对象,通过属性名获取JavaBean的方法对象(读) Method methodX = pd.getReadMethod(); //3.通过方法对象获取属性值 Object retVal = methodX.invoke(p1); return retVal; } private static void setPropertiesEasy(Object p1, Object y) throws Exception{//简单方式 PropertyDescriptor pd = new PropertyDescriptor("y", p1.getClass());//通过属性名和类的字节码初始化一个PropertyDescriptor对象 Method methodSetY = pd.getWriteMethod();//通过属性名获取JavaBean的方法对象(写) methodSetY.invoke(p1, y);//修改属性值 } private static void setPropertiesComplex(Object p1, Object y) throws Exception{//复杂内省操作JavaBean //1.通过Introspector.getBeanInfo()方法获取bean的对象(BeanInfo),传递JavaBean的字节码参数 BeanInfo beanInfo = Introspector.getBeanInfo(p1.getClass()); //2.获取到BeanInfo之后获得所有的JavaBean的Properties PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors(); //3.通过迭代、反射来修改JavaBean的属性 for(PropertyDescriptor pd : pds){ if(pd.getName().equals("y")){//匹配属性值 Method methodSetY = pd.getWriteMethod(); methodSetY.invoke(p1, 10); } } } }
四:注解
1.概述:注解相当于一种标记,没加则等于没有某种标记,以后javac编译器开发工具和其它程序可以用反 射来了解你的类
以及各种元素上有无何种标及,看你有什么标记就去干相应的事,标记可以加在包,类,字段,方法,方法的参数以及局部变量上。
2.常用三注解:
interface Deprecated用 @Deprecated 注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。
interface Override
表示一个方法声明打算重写超类中的另一个方法声明。
interface SuppressWarnings
指示应该在注释元素(以及包含在该注释元素中的所有程序元素)中取消显示指定的编译器警告。
3.Retention:指示注释类型的注释要保留多久,如果注释类型声明中不存在Retention注释,则保留策率默认为 RetentionPolicy.Class
4.注解生命周期:
RetentionPolicy.SOURCE: java源文件,即javac编译之后就不存在啦。RetentionPolicy.CLASS: class文件,即编译后还存在字节码中,但不在内存中。
RetentionPolicy.RUNTIME:内存中的字节码,即一直保存在内存中。
5.为注解添加高级属性:
数组类型的属性:注解中:int []arrarAttr() default{1,2,3};
类中给注解赋值:@注解名(arrayAttr={2,3,4})
如果数组属性中只有一个元素,这时候属性值部分可以省略大括号
枚举类型的属性:
注解类型的属性:
示例代码如下:
1.定义一个注解
package com.JavaSE.Annotation; import java.lang.annotation.ElementType; @Retention(RetentionPolicy.RUNTIME)//运行时的注解,表明一直与源程序被保存到内存中 @Target({ElementType.METHOD,ElementType.TYPE})//目标,即注解所放的位置为方法和类型前面 public @interface Annotation {//注解中可以放8个类型得的数据 //属性 String str() default "defaultString"; String value() ; int data() default 1; int[] array() default{1,2,3}; MetaAnnotation annotation() default @MetaAnnotation("注解属性"); Lamp Enum() default Lamp.GREEN; }
2.测试注解
package com.JavaSE.Annotation; @Annotation(value="123") public class AnnotationTest { @Annotation(value="123") public static void main(String[] args) { if(AnnotationTest.class.isAnnotationPresent(Annotation.class)){//判断当前类是否加了注解 Annotation annotation = AnnotationTest.class.getAnnotation(Annotation.class);//通过反射得到注解 //打印一些注解的信息 System.out.println(annotation.data()); System.out.println(annotation.value()); System.out.println(annotation.str()); System.out.println(annotation.annotation()); System.out.println(annotation.annotationType()); System.out.println(annotation.array().length); System.out.println(annotation.Enum()); } } }
3.定义一个枚举
package com.JavaSE.Annotation; public enum Lamp { RED,GREEN,YELLOW; }
4.定义一个注解
package com.JavaSE.Annotation; public @interface MetaAnnotation { String value(); }
总结:
通过反射可以理解Java虚拟机是怎样操作字节码的,由于字节码在Java虚拟机的唯一性,进而更方便的可以拿到自己想要的对象,在struts框架和Hibernate框架中可以体现出反射的作用。
枚举可以让一个类的实例固定化,从而对比现实生活中一些固定的事物,如交通灯。
内省可以自由操控实体Bean的属性,可以再Hibernate中得以体现。
注解也是在Hibernate中体现的,由于注解原理的实现很像一个类,因此可以简化Hibernate的操作。
相关文章推荐
- 黑马程序员_java基础加强_静态导入_反射_枚举_注解_内省_泛型_代理
- 黑马程序员_java_基础加强_静态导入_反射_枚举_注解_内省_泛型
- java高新技术_枚举反射内省(黑马程序员)
- Java - 基础增强 - 增强for - 可变参数 - 枚举 - 反射 - 内省 - 泛型
- java 反射,注解,泛型,内省(高级知识点)
- java基础加强(枚举,反射,增强for循环,可变参数,自动拆箱装箱,内省)
- Java基础---Java---基础加强---内省的简单运用、注解的定义与反射调用、 自定义注解及其应用、泛型及泛型的高级应用、泛型集合的综合
- 10_Java高新_枚举-反射-注解-泛型
- 黑马程序员——Java高新技术---枚举、内省、类加载器、注解
- java笔记1(策略、代理模式、枚举、反射、注解)
- Java基础---Java---基础加强---内省的简单运用、注解的定义与反射调用、 自定义注解及其应用、泛型及泛型的高级应用、泛型集合的综合
- Java基础加强_Eclipse、枚举、反射、注解、泛型、类加载器、动态代理
- 黑马程序员——java高新(枚举、内省、注解、类加载器)
- Java集合、反射、内省、注解总结
- 随笔-java注解,反射,枚举
- 黑马程序员--高新技术--枚举、反射、注解、类加载器、内省
- 黑马程序员_java高新技术(1)枚举、反射、内省
- JAVA 与 MyCat(5) 类的加载 Java内省/反射机制 注解Annotation 详解
- 黑马程序员_王康 java反射,枚举,注解
- java反射、内省、注解