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(实际参数)进行对象的初始化。

代码示例如下:
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的操作。




  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值