java反射

动态编程

程序运行过程中,可以改变程序的结构或变量类型。
虽然java不是动态语言,但是java具有一定的动态性,可以利用反射机制,字节码操作获得类似动态语言的操作。
反射是很重要的,他是java里的大哥级别人物,好多不能做的他都可以做。

反射机制

反射机制是指可以在运行时加载,探知,使用编译期间完全未知的类。

也就是说,利用反射机制,可以实例化一个在编译时只知道名字的类,不用new 创建对象。

Class c = Class.forName("包名.类名");

这就是一个常用的利用反射获取对象class的方法,对象的class里面包含了完整的类的结构信息,我们可以通过这个class,实例化一个对象。
同一个类只会被加载一次,只对应一个Class对象,多次获得Class对象,都是同一个Class对象(地址相同)

对象是表示或封装一些数据。一个类被加载之后,jvm会创建一个对应该类的Class对象,类的整个结构信息会被放到对应的Class对象中

获取Class对象的方法

  • Class.forName(“包名.类名”);
  • 类名.class; (基本数据类型,数组都可以,数组Class对象是否相同看维度,不看大小
  • 对象名.getClass();

获得Class对象之后的操作

首先我们来看一下我们获得Class对象的名字

System.out.println(clazz.getName());//包名+类名
System.out.println(clazz.getSimpleName());//类名

getName()获得类的包名+类名,getSimpleName()见名知意,返回简单的类名,即只返回类名。

接着我们可以看一下类里面的属性

Field[] fields = clazz.getFields();//返回public的属性
System.out.println(fields.length);
fields = clazz.getDeclaredFields();//返回所有的属性
System.out.println(fields.length);
Field field = clazz.getField("name");//必须是public的属性
System.out.println(field);

getFields()返回类中所有的public属性,如果想要看到所有的属性,可以用getDeclaredFields()方法。如果只想看到某一指定的方法怎么办,getFields(String name),getDeclaredFields(String name)能实现这一想法。

接下来是类的方法,方法可以看作广义的属性,所以方法差不多,只需要把Field改成Method就可以了

Method method = clazz.getMethod("getId", null);//第二个是参数,如果有,则写参数对应的Class对象
Method mm = clazz.getMethod("setId", int.class);
System.out.println(method);

方法是有参数的,所以getMethod()和vgetDeclaredMethod()方法多了个参数,表明你想看的那个方法的参数,如果没有,就写null,如果用,参数是方法参数的Class对象,有几个写几个。

然后是构造器,构造器也是方法,所以方法相同

Constructor con = clazz.getConstructor(int.class,String.class,int.class);
System.out.println(con);

获取到了构造器,当然要用它实例化一个对象啦,实例化对象用newInstance()方法,Class对象自带一个newInstance()方法,调用类的无参构造器,我们也可以用geiConstructor()获得了一个构造器之后再调用构造器的newInstance()方法。

user user = (user)clazz.newInstance();//调用无参构造器
user = (user)clazz.getConstructor(int.class,String.class,int.class).newInstance(3969,"王二狗",21);
System.out.println(user);

获得了构造器能构造对象,那么获得了方法和属性能干什么呢,答:可以给操作对象的方法和属性。Method类有invoke(Object obj, Object… args)方法,obj是要操作的对象,args是这个方法的参数,用这个方法可以操作对象中对应的方法;Field类中用get和set方法,能操作对象中对应的属性。但是操作的都是共有方法和属性,怎么能操作私有的呢,反射是大哥,得给他找个方法—setAccessible(boolean),是否关闭安全检查,参数为true,就可以无差别的操作共有和私有方法属性了。

Method mm = clazz.getDeclaredMethod("setName", String.class);
mm.invoke(user, "王星宇");
System.out.println(user);
Field ff = clazz.getDeclaredField("age");
ff.setAccessible(true);//不用做安全检查,直接访问,忽略private
ff.set(user, 18);
System.out.println(ff.get(user));

反射机制的性能问题

使用反射会使程序有性能问题(一般30倍的普通方法效率【用反射调用:使用对象调用】)
可以使用setAccessiable提高性能(提高4倍)
这也可以理解,禁用了安全检查,当然会提高速度。

下面的一般用不到

反射操作泛型*

java采用的是泛型擦除机制来引入的泛型——java中的泛型仅仅是给编译器使用,确保了数据的安全性和免去强制类型转换的麻烦,但是,一旦编译完成,所有跟泛型有关的数据全部擦除。
泛型和反射可谓两个次元的人。可是,反射想要操作泛型怎么办呢,别急,为了能通过反射操作这类类型(以迎合实际开发需要),Java就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类型中的类型但是又和原始类型起名的类型,也即是说,给反射大哥一下子来了四种“泛型”。
在这里插入图片描述
测试ParameterizedType:

public class testType {
	public void test01(Map<String,user>map,List<user> list) {
		System.out.println("testType.test01");
	}
	public Map<Integer,user> test02(){
		System.out.println("testType.test02");
		return null;
	}
	public static void main(String[] args) {
		try {
			//获得test01方法
			Method m = testType.class.getMethod("test01", Map.class,List.class);
			//获得参数并用泛型存起来  type是另外四个泛型的接口
			Type[] t = m.getGenericParameterTypes();
			for(Type paramType : t) {
				//输出参数
				System.out.println("#" + paramType);
				//如果是容器类型的泛型
				if(paramType instanceof ParameterizedType) {
					//获得容器的泛型<String,user>
					Type[] genericTypes = ((ParameterizedType) paramType).getActualTypeArguments();
					//循环输出
					for(Type genericType : genericTypes) {
						System.out.println("泛型类型" + genericType);
					}
				}
			}
		}catch (Exception e) {
		}
	}
}

反射操作注解

既然反射这个大哥想要操作注解,那么注解必须用上全力呀,让自己的有效时间达到Runtime才行。

@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface table {
	String value(); // 修饰的类跟哪个表对应
}
@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Field {
	//类每个属性对应的列名
	String columnName();
	String type();
	int length();
}

被注解的类

@table("tb_student")
public class student {
	@Field(columnName = "id",type = "int",length = 10)
	private int id;
	@Field(columnName = "sname",type = "varchar",length = 10)
	private String name;
	@Field(columnName = "age",type = "int",length = 3)
	private int age;
}

使用反射操作注解:

public class testAnnotation {
	public static void main(String[] args) {
		try {
			Class clazz = Class.forName("TestAnnotation.student");
			//获得类的所有注解
			Annotation[] annotations = clazz.getAnnotations();
			for(Annotation a : annotations) {
				System.out.println(a);
			}
			//获得指定注解的值
			table table = (TestAnnotation.table) clazz.getAnnotation(table.class);
			System.out.println(table.value());
			//获得属性的值
			java.lang.reflect.Field field = clazz.getDeclaredField("name");
			Field f = field.getAnnotation(Field.class);
			System.out.println(f.columnName() + "," + f.type() + "," + f.length());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

总结

反射归根结底就是在操作Class对象,这在我们之前一直是JVM在做。Class对象相当于类的模子,尽管反射降低了运行效率,但是提高了开发效率,就像活字印刷术,可能制作起来麻烦,但是用起来很方便,而且快。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值