黑马程序员技术交流社区

标题: 关于Java之反射、枚举、内省、注解,网络上不错的分享 [打印本页]

作者: 塞肥肥塞牙人    时间: 2014-9-5 22:56
标题: 关于Java之反射、枚举、内省、注解,网络上不错的分享
一:反射技术:         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.class
                           4.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(实际参数)进行对象的初始化。



作者: 塞肥肥塞牙人    时间: 2014-9-5 22:58
代码示例如下:
1.定义被反射的类:
[java] view plaincopyprint?
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;  
    }     
}  

作者: 塞肥肥塞牙人    时间: 2014-9-5 23:01
二:枚举:
        1.概述:枚举就是一个特殊的java类,可以定义属性、方法、构造函数、实现接口、继承类,对象的某个属性的值不能是                             任意的,必须为固定的一组取值其中的某一个,因此出现了枚举。
        2.直接来代码最合适:
代码如下:

1.定义枚举
[java] view plaincopyprint?
package com.JavaSE.Enum;  
//枚举  
public enum WeekDayEnum {  
    Sun,Mon,Tue,Wen,Thr,Fri,Sat;  
}  

作者: zhangxun    时间: 2014-9-5 23:02
好东西收藏了
作者: 塞肥肥塞牙人    时间: 2014-9-5 23:02
2.枚举的原理
[java] view plaincopyprint?
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";   
        }  
    }  
}  
作者: 塞肥肥塞牙人    时间: 2014-9-5 23:04
三:内省:即对JavaBean的操作
        1.说明:对象JavaBean的简单内省操作(简单方便:PropertyDescriptor)和复 杂操作(稍微复杂点:IntroSpector)
        2.步奏:
                   2. 1.创建PropertyDescriptor对象
                   2.2.获取JavaBean的方法对象
                   2.3.通过方法对象获取属性值
                   2.4.修改属性值
代码如下:
1.JavaBean:
[java] view plaincopyprint?
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;  
    }  
  
}  
作者: 塞肥肥塞牙人    时间: 2014-9-5 23:05
2.对JavaBean的操作
[java] view plaincopyprint?
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);  
            }  
        }  
    }  
}  
作者: 大山    时间: 2014-9-5 23:06
看的好快啊,还没看到,不过快了
作者: 塞肥肥塞牙人    时间: 2014-9-5 23:07
四:注解
     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.定义一个注解
[java] view plaincopyprint?
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;   
      
}  

作者: 塞肥肥塞牙人    时间: 2014-9-5 23:09
2.测试注解
[java] view plaincopyprint?
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());  
        }  
         
    }  
  
}  
[java] view plaincopyprint?
3.定义一个枚举  
[java] view plaincopyprint?
package com.JavaSE.Annotation;  
  
public enum Lamp {  
    RED,GREEN,YELLOW;  
  
}  
[java] view plaincopyprint?
4.定义一个注解  
[java] view plaincopyprint?
package com.JavaSE.Annotation;  
  
public @interface MetaAnnotation {  
    String value();  
}  

总结:
        通过反射可以理解Java虚拟机是怎样操作字节码的,由于字节码在Java虚拟机的唯一性,进而更方便的可以拿到自己想要的对象,在struts框架和Hibernate框架中可以体现出反射的作用。
        枚举可以让一个类的实例固定化,从而对比现实生活中一些固定的事物,如交通灯。
        内省可以自由操控实体Bean的属性,可以再Hibernate中得以体现。
        注解也是在Hibernate中体现的,由于注解原理的实现很像一个类,因此可以简化Hibernate的操作。
作者: liqiaohui    时间: 2014-9-5 23:09
好东西
作者: 塞肥肥塞牙人    时间: 2014-9-5 23:10
以上内容都是网上看到的,觉得不错帖子,让我这样粘过来确实卖相没了,望见谅哈
作者: 黄凯旋    时间: 2014-9-5 23:18
这么多么
作者: xplcc    时间: 2014-9-6 00:21
直接做成html格式下载就好了
作者: ╃→梅飛揚之城    时间: 2014-9-6 00:23
不错的东西,收下
作者: 陈永赛    时间: 2014-9-6 00:45
谢谢分享啦,但楼主发的代码好乱···
作者: dreamseekerkun    时间: 2014-9-6 01:11
感谢分享,刚好看到
作者: ┣┫流枫    时间: 2014-9-6 09:23
好乱啊  没看下去的欲望了
作者: 毛富强    时间: 2014-9-6 10:34
谢谢 分享。。。学习了
作者: 塞肥肥塞牙人    时间: 2014-9-6 10:40
以后会注意的,我也觉得看起来挺乱的
作者: Mrzn    时间: 2014-9-6 10:49
谢谢分享了。
作者: 怀念黑海岸    时间: 2014-9-6 12:15
排版差评,眼睛花了。。。
作者: lq你微笑时好美    时间: 2014-9-6 12:30
马克,还没学到
作者: 戏言丶    时间: 2014-9-6 12:31
首先还是非常感谢楼主的分享的,但是,楼主你在发表前应该注意一下排版,这是一个很严重的问题的
作者: sk0806    时间: 2014-9-6 12:32
谢谢分享哈,我们要取,也得复制粘贴了
作者: 塞肥肥塞牙人    时间: 2014-9-6 12:43
那我以后应该怎么发才能够方便一点啊
作者: 梁泽冕    时间: 2014-9-6 15:58
来的及时啊,大多数文章都是一大截,找个简单明了的都难。
作者: 不服™け你咬我    时间: 2014-9-6 16:08
好东西,,
作者: F@C    时间: 2014-9-6 17:14
进来学习




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2