Java反射

定义:反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

即:在"运行时",通过反射机制可以动态获得到和该类型相关的各种信息。

(1) Class类型 java.lang.Class类
Class是对java中所有类型的抽象。即一个Class类型对象可以表示出java中任意一种类型。
每种类型在加载到内存后,内存中都会生产一个与之对应的Class类型对象(有且只有一个),用来表示该类型。
每个类型都有且只有一个Class类型对象与之对应,通过这个Class类型对象就可以获得到该类型中的各种信息。
Class类是Java反射的入口.
1.表示基本类型

            Class c = int.class;
			System.out.println(c.isPrimitive());//true
			System.out.println(c.getName());//int
			
			//注:其他基本类型的情况类似

2.表示类类型

//注:s.getClass()方法返回的是变量s所指向对象的实现类型的Class对象。
			Student s = new Student();
			Class c1 = s.getClass();
			Class c2 = Student.class;
			System.out.println(c1 == c2);//true
			
			//p指向的对象实际类型是Student
			Person p = new Student();
			Class c1 = p.getClass();//c1表示Student类型
			Class c2 = Person.class;//c2表示Person类型
			System.out.println(c1 == c2);//false

3.表示接口类型

            Action a = new Student();
			Class c1 = a.getClass();//c1表示Student类型
			Class c2 = Action.class;//c2表示Action类型
			System.out.println(c1 == c2);//false

			System.out.println(c2.isInterface());//true

4.表示数组类型

            int[] a = new int[4];
			Class c1 = a.getClass();
			Class c2 = int[].class;
			System.out.println(c1 == c2);//true
			System.out.println(c1.isArray());//true

			Class c3 = c1.getComponentType();//c3表示该数组是使用什么类型声明出来的
			System.out.println(c3.getName());//int
			

			
			Student[] a = new Student[4];
			Class c1 = a.getClass();
			Class c2 = Student[].class;
			System.out.println(c1 == c2);//true
			System.out.println(c1.isArray());//true

			Class c3 = c1.getComponentType();//c3表示该数组是使用什么类型声明出来的
			System.out.println(c3.getName());//com.briup.test.Student

(2)获得一个类类型的Class对象的三种方式
以上几种情况中,所以最多的还是类类型的Class对象
1.使用对象调用getClass方法获得

      //getClass是Object中的final修饰的方法,每个对象都可以调用而且不能重写
			Student s = new Student();
			Class c = s.getClass();

2.使用类名获得

Class c = Student.class;

3.使用Class类中的forName方法获得

        //这种方法很灵活,只需一个String类型参数即可
		//而String类型的数据改变起来很容易
		//注意该方法是会抛出异常的
		Class c = Class.forName("com.briup.test.Student");

注:以上三种方法获得的同一个对象(==比较),因为每个类型内存都有且只有一个Class类型对象

(3)反射机制中的常见类的含义
java.lang包下:
Class 类 对java中所有类型抽象而得来的
package类 对java中所有包抽象而得来的

java.lang.reflect包下:
Modifier 类 对java中所有修饰符抽象而得来的
Field 类 对java中所有属性抽象而得来的
Method 类 对java中所有方法抽象而得来的
Constructor 类 对java中所有构造器抽象而得来的
Array 类 提供了对数组对象的动态访问
ParameterizedType接口 在反射中表示参数化类型
例如:List Point<Long,Long>等这种带泛型的类型

(4)使用Class类型对象获得类中的信息
1.获得该类所处的包的信息

          Student s = new Student();
	      Class c = s.getClass();
  	      System.out.println(c.getPackage().getName());

2.获得该类的修饰符信息

           //每个修饰符对应一个int值
			//如果有多个修饰符则int值相加
			Student s = new Student();
			Class c = s.getClass();
			System.out.println(c.getModifiers());

			System.out.println(Modifier.PUBLIC);
			System.out.println(Modifier.FINAL);

3.获得该类的名字

           Student s = new Student();
			Class c = s.getClass();
			System.out.println(c.getName());

4.获得该类的父类的Class对象

           Student s = new Student();
			Class c = s.getClass();
			//superclass表示其父类型的Class对象
			Class superclass = c.getSuperclass();
			System.out.println(superclass.getName());

			//例如:
			Class c = Object.class;
			Class superclass = c.getSuperclass();
			System.out.println(superclass.getName());
			//运行报错,因为Object没有父类
			

			//例如:
			//判断c1是不是c2的子类型
			//判断c3是不是c2的子类型
			Class c1 = Student.class;
			Class c2 = Person.class;
			Class c3 = String.class
			System.out.println(c2.isAssignableFrom(c1));//true
			System.out.println(c2.isAssignableFrom(c3));//false

5.获得该类所实现的接口类型的Class对象

            Student s = new Student();
			Class c = s.getClass();
			Class[] interfaces = c.getInterfaces();
			for(Class clazz:interfaces){
				System.out.println(clazz.getName());
			}

			例如:
			//判断c1是不是c2的实现类
			//判断c3是不是c2的实现类
			Class c1 = Student.class;
			Class c2 = Action.class;
			Class c3 = String.class
			System.out.println(c2.isAssignableFrom(c1));//true
			System.out.println(c2.isAssignableFrom(c3));//false

6.获得该类中所有的属性

            Student s = new Student();
			Class c = s.getClass();
			Field[] declaredFields = c.getDeclaredFields();
			
			for(Field f:declaredFields){
				System.out.println(f.getModifiers());
				System.out.println(f.getType().getName());
				System.out.println(f.getName());
			}
			//注:
			//getDeclaredFields()方法返回类中声明的属性,包括私有的
			//getFields()方法只返回类中public修饰的属性,包括继承的
	
			
			//例如:
			//获得某个指定的属性(也包括私有属性)
			Student s = new Student();
			Class c = s.getClass();
			Field f = c.getDeclaredField("score");
			System.out.println(f.getModifiers());
			System.out.println(f.getType().getName());
			System.out.println(f.getName());

7.获得该类中所有的方法

            Student s = new Student();
			Class c = s.getClass();
			Method[] declaredMethods = c.getDeclaredMethods();
			for(Method m:declaredMethods){
				System.out.println(m.getModifiers());
				System.out.println(m.getReturnType().getName());
				System.out.println(m.getName());
				System.out.println(Arrays.toString(m.getParameterTypes()));
				System.out.println(Arrays.toString(m.getExceptionTypes()));
			}

			//注:
			//getDeclaredMethods()方法返回类中声明的方法,包括私有的
			//getMethods()方法只返回类中public修饰的方法,包括继承的
			

			例如:
			//获得某个指定的方法(也包括私有方法)
			Student s = new Student();
			Class c = s.getClass();
			Method m = c.getDeclaredMethod("print");
			System.out.println(m.getModifiers());
			System.out.println(m.getReturnType().getName());
			System.out.println(m.getName());
			System.out.println(Arrays.toString(m.getParameterTypes()));
			System.out.println(Arrays.toString(m.getExceptionTypes()));

8.获得该类中所有的构造器

            Student s = new Student();
			Class c = s.getClass();
			Constructor[] declaredConstructors = c.getDeclaredConstructors();
			for(Constructor con:declaredConstructors){
				System.out.println(con.getModifiers());
				System.out.println(con.getName());
				System.out.println(Arrays.toString(con.getParameterTypes()));
				System.out.println(Arrays.toString(con.getExceptionTypes()));
			}

			//注:
			//getDeclaredConstructors()方法返回类中所有构造器
			//getConstructors()方法只返回类中public修饰的构造器
			
			
			//例如:
			//获得某个指定的构造器(也包括私有构造器)
			Student s = new Student();
			Class c = s.getClass();
			Constructor con = c.getDeclaredConstructor(double.class);
			System.out.println(con.getModifiers());
			System.out.println(con.getName());
			System.out.println(Arrays.toString(con.getParameterTypes()));
			System.out.println(Arrays.toString(con.getExceptionTypes()));

9.获得父类型中的泛型的真实类型
因为泛型类的泛型参数在编译期会被擦除,所以我们不能再运行期间直接拿到该泛型的实际类型,但是可以通过子类的Class对象来获取父类的泛型类型。
例如: 不通过子类不能获得泛型实际类型

public class GenericTest<T,S>{
				public T name;
				public S say(T t,S s){
					return s;
				}
			}
			
			main:
				GenericTest<String,Integer> t = new GenericTest<String,Integer>();
				Class c = t.getClass();
				Field field = c.getDeclaredField("name");
				System.out.println(field.getType());
				System.out.println(field.getGenericType());
				//输出结果:
					class java.lang.Object
					T
				
				System.out.println("-------------------------");
				Method method = c.getMethod("say", Object.class,Object.class);
				System.out.println(method.getReturnType());
				System.out.println(method.getGenericReturnType());
				//输出结果:
				class java.lang.Object
				S

				System.out.println("-------------------------");
				System.out.println(Arrays.toString(method.getParameterTypes()));
				System.out.println(Arrays.toString(method.getGenericParameterTypes()));
				//输出结果:
				[class java.lang.Object, class java.lang.Object]
				[T, S]
			

例如: 通过子类可以获得父类中泛型的实际类型

public class GenericTest<T,S>{
				public T name;
				public S say(T t,S s){
					return s;
				}
			}
			public class Sub entends GenericTest<String,Integer>{}
			
			main:
				Class c = Sub.class;

				//获得父类类型,包含泛型参数信息 
				Type superType = c.getGenericSuperclass();

				//判断父类类型是不是属于ParameterizedType类型
				//ParameterizedType表示带泛型的类型
				if(superType instanceof ParameterizedType){
					//强转,并调用方法获得泛型参数的实例类型
					ParameterizedType pt = (ParameterizedType)superType;

					Type[] actualTypeArguments = pt.getActualTypeArguments();

					//循环遍历,并强转为Class类型,因为Type接口中没有任何方法
					for(Type t:actualTypeArguments){
						Class clazz = (Class)t;
						System.out.println(clazz.getName());
					}
				}
				

例如: 通过子类可以获得实现接口中泛型的实际类型

                Class c = Sub.class;
				Type[] types = c.getGenericInterfaces();
				for(Type t:types){
					if(t instanceof ParameterizedType){
						ParameterizedType pt = (ParameterizedType)t;

						Type[] actualTypeArguments = pt.getActualTypeArguments();

						for(Type type:actualTypeArguments){
							Class clazz = (Class)type;
							System.out.println(clazz.getName());
						}

					}
				}

10.获得类中的注解
使用反射也可以获得类中的注解(在之后的内容在来了解)

(5)反射中的常用操作

public class Student{
			private long id;
			private String name;

			private static int age;
			
			get/set

			public static void say(){
				System.out.println("say..");
			}

		}

1.使用反射的方式调用构造器创建类的对象
默认方式:必须调用无参构造器

            Class c = Student.class;
			Student s = (Student)c.newInstance();

通用方式:获得构造器对象,并调用该构造器

注:getConstructor方法和newInstance方法的参数都是可变参数
例如:获得无参构造器并调用创建对象

            Class c = Student.class;
			Constructor constructor = c.getConstructor();
			Student o = (Student)constructor.newInstance();
			System.out.println(o);

例如:获得有参构造器并调用创建对象

            Class c = Student.class;
			Constructor constructor = c.getConstructor(long.class,String.class);
			Student o = (Student)constructor.newInstance(1L,"tom");
			System.out.println(o);

2.使用反射的方式访问对象中的属性
例如:

           Student s = new Student();
			Class c = s.getClass();
			Field[] declaredFields = c.getDeclaredFields();
			for(Field f:declaredFields){

				//设置私有属性可以被访问
				f.setAccessible(true);

				//判断属性是否为static,静态属性的访问不需要对象
				if(Modifier.isStatic(f.getModifiers())){
					System.out.println(f.getName() +" = "+f.get(null));
				}else{
					System.out.println(f.getName() +" = "+f.get(s));
				}
			}

			//注:Field类中的get方法可以获得属性值,set方法可以给属性设置值。
		

3.使用反射的方式调用对象中的方法
例如:

            Student s = new Student();
			Class c = s.getClass();
			
			Method m1 = c.getMethod("setName",String.class);
			//m1表示Student中的setName方法
			//调用对象s中的m1方法,并且传参"tom"
			//s.setName("tom");
			m1.invoke(s, "tom");
			
			Method m2 = c.getMethod("getName");
			String name = (String)m2.invoke(s);
			System.out.println("name = "+name);
			
			//调用静态方法 不需要对象
			Method m3 = c.getMethod("say");
			m3.invoke(null);

4.使用反射的方式动态操作数组

:
			Object[] o1 = new int[1];//编译报错
			long[] 	 o2 = new int[1];//编译报错
			int[]	 o3 = new int[1];//编译通过
			
			Object[] o1 = new 任意引用类型[1];//编译通过

			例如:
			//要求:传任意类型"数组",把数组长度扩大1倍并返回
			//注意这里不能收Object[],
			//因为Object[] o = new Integer[4];编译通过
			//但是Object[] o = new int[4];	  编译报错
			//那么使用Object类中就可以接收任意类型的数组了
			public static Object arrayCopy(Object obj){
				//代码
			}

			实现:
			public static Object arrayCopy(Object obj){
				Class c = obj.getClass();
				Object newArray = null;
				if(c.isArray()){
					int len = Array.getLength(obj);
					Class<?> type = c.getComponentType();
					newArray = Array.newInstance(type, len*2);
					for(int i=0;i<len;i++){
						Object value = Array.get(obj, i);
						Array.set(newArray, i, value);
					}
				}
				return newArray;
			}
			

总结:
java的反射机制就是增加程序的灵活性,避免将程序写死到代码里,
实例化一个 person()对象, 不使用反射, new person(); 如果想变成实例化其他类, 那么必须修改源代码,并重新编译。
使用反射: class.forName(“person”).newInstance(); 而且这个类描述可以写到配置文件中,如 **.xml, 这样如果想实例化其他类,
只要修改配置文件的"类描述"就可以了,不需要重新修改代码并编译

优点:增加程序的灵活性,避免将代码写死到程序里;许多框架功能是以反射为基础构建的,比如spring框架,mybatis框架等

缺点:1、缺点是对性能有影响。性能问题,使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
2、安全限制
3、内部暴露,由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值