反射

什么是反射?

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。 

反射是一种动态获取对象调用的一种机制,对java语言灵活性发挥很大。

作用: 能够在程序运行时判断任意一个对象所属哪个类,获取该类的属性、方法、构造方法并在任意的时刻调用属性方法。很多框架底层都使用到反射机制: Spring MyBatis 动态代理等都使用了反射技术。

简而言之,正常的编码逻辑是从类开始,然后属性,构造器,方法等,只有在熟知类的情况下才会使用相应的内容。而反射就是通过特定方式去获取对象、方法、属性等,即使在对类完全陌生的情况下也能获取到类中所有内容,并使用其方法等东西的机制。

首先回顾一下获取对象的方式:

1.通过调用类的构造方法获取对象。

2.通过反射获取对象

3.通过反序列化获取对象

4.通过继承克隆接口,调用克隆方法获取对象

5.通过静态工厂创建对象的方法

Class类

Java在将.class字节码文件载入时,JVM将产生一个java.lang.Class对象代表该.class字节码文件(生成的class文件的个数和工程中拥有的类的个数相同,即使是内部类也会有一个相应的class文件),从该Class对象中可以获得类的许多基本信息,这就是反射机制。所以要想完成反射操作,就必须首先认识Class类。

反射机制所需的类主要有java.lang包中的Class类和java.lang.reflet包中的Constructor类、Field类、Method类和Parameter类。Class类是一个比较特殊的类,它是反射机制的基础Class类 的对象表示正在运行的Java程序中的类或接口,也就是任何一个类被加载时,即将类的.class文件(字 节码文件)读入内存的同时,都自动为之创建一个java.lang.Class对象。Class类没有公共构造方 法,其对象是JVM在加载类时通过调用类加载器中的defineClass()方法创建的,因此不能显式地创 建一个Class对象。通过这个Class对象,才可以获得该对象的其他信息。下表列出了Class类的一些常 用方法。

前面那句Class类是反射的基础特地打了红色标记,由此也可以知道反射中Class对象的重要性。

由此我们需要知道创建Class类对象的三种方式:

1.使用类对象.getClass();

2.使用类.Class();

3.使用Class.forName("包名.类名");

package com.reflect;

import java.util.List;
import java.lang.reflect.Constructor;

import com.pojo._1_n.Department;
import com.pojo._1_n.Employee;

public class Demo {
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException {
		//反射的基础是Class类
		//学习如何创建Class对象
		//方式1:通过Object类中的getClass()方法
		Employee e1=new Employee();
		Class<Employee> c1=(Class<Employee>) e1.getClass();
		System.out.println(c1);
		
		//方式2:class属性
		Class<Employee> c2=Employee.class;
		System.out.println(c2);
		Class<Integer> c3=int.class;
		System.out.println(c3);
		Class<List> c4=List.class;
		System.out.println(c4);
		
		
		//方式3:Class类的静态方法forName(String className)
		try {
			Class c5=Class.forName("com.pojo._1_n.Employee");
			System.out.println(c5);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		System.out.println("----------");
	}

}

当获取到Class对象之后,我们就可以利用反射进行很多操作。

例如获取类中的属性名:

涉及到Field类相关方法:常用的是getField和getDeclaredFields

package com.reflect;

import java.lang.reflect.Field;

import com.pojo._1_n.Department;

public class Demo3 {
	public static void main(String[] args) throws Exception {
		Class<Department> deptClass=Department.class;
		Field[] f=deptClass.getDeclaredFields();//获取类中的方法
		//获取指定字段的属性信息(因此底层需要类的属性名和数据库中的字段名相同)
		Field name = deptClass.getDeclaredField("id");
		System.out.println("-------");
		for (Field x : f) {
			System.out.println(x);
			//获取属性的修饰符
			System.out.println(x.getModifiers());
			//获取属性的类型
			System.out.println(x.getType());
			//获取属性的名称
			System.out.println(x.getName());
			System.out.println("-------");
		}
	}

}

例如获取类中的构造器:

涉及到Constructor类相关方法:常用的是getConstructor和getDeclaredConstructors

getConstructor和getDeclaredConstructors的区别:

    1.前者只能获取public修饰的构造器,而后者可以获取所有构造器

    2.前者返回的是一个Constructor对象,但是后者返回的是一个Constructor数组

    3.在获取到类中构造器中参数类型时,可以通过后者获取特定构造器(虽然听起来操作有点怪,但是确实可以)

当我们获取到类构造器后,当然不能忘记构造器创建对象的作用,于是可以通过获取到的构造器对象调用newInstance方法来创建该类的对象(Class类中在获取到类对象之后,也可以通过调用newInstance创建对象,但是两个方法虽然同名,但是一个是Class类中的方法,一个是Constructor类中的对象)。

package com.reflect;

import java.util.List;
import java.lang.reflect.Constructor;

import com.pojo._1_n.Department;
import com.pojo._1_n.Employee;

public class Demo {
	
	public static void main(String[] args) throws NoSuchMethodException, SecurityException {
		//获取构造方法
		Class c=Department.class;
		Constructor<Department>[] c6=c.getDeclaredConstructors();//获取所有的构造方法
		for (Constructor<Department> x : c6) {
			System.out.println(x);
		}
		System.out.println("----------");
		Constructor<Department>[] c7=c.getConstructors();//获取public的构造方法
		for (Constructor<Department> x : c7) {
			System.out.println(x);
		}
		System.out.println("----------");
		Constructor c8=c.getDeclaredConstructor();//获取特定的构造方法(无参)
		System.out.println(c8);
		Constructor c9=c.getDeclaredConstructor(Integer.class,String.class,String.class,List.class);//获取特定的构造方法(有参)
		System.out.println(c9);

//反射获取对象
		//通过获取类型-->调用newInstance方法创建对象
		//newInstance底层会默认调用newInstance括号中包含的参数的构造方法,如果没有对应的构造方法则报出异常
		Class<Department> c=Department.class;
		Department d=c.newInstance();
		System.out.println(d);
		
		//2.通过Constructor类的方法
		Constructor<Department> gzq=c.getConstructor();
		Department d2=gzq.newInstance();
		System.out.println(d2);
		
		//有值
		Constructor<Department> gzq2=c.getConstructor(Integer.class,String.class,String.class,List.class);
		Department d3=gzq2.newInstance(10,"研发部","武汉",null);
		System.out.println(d3);
	}
}

除了获得属性(Field类)和构造器(Constructor类)还能获取到类的方法(Method类)。

涉及到Method类中常用的方法有:

                             getMethod(String name,Class<?> parameterTypes)和getDeclaredMethod(String name,Class<?> parameterTypes),获取一个方法Method,其中参数一是方法的名字,参数二是方法的参数列表类型。

                             getMethods()和getDeclaredMethods(),获取多个方法Method[]。

前者是获取公有的方法,后者是获取所有方法。

package com.reflect;

import java.lang.reflect.Method;
import java.util.Arrays;

public class Demo6 {
	public static void main(String[] args) throws Exception {
		// 获取方法的类型
		Class<MyMethod> myclass = MyMethod.class;

		// 利用反射来查看这个类有哪些方法
		Method[] methods = myclass.getDeclaredMethods();
		// 输出所有的方法
		Arrays.stream(methods).forEach(System.out::println);
		// 使用对象.方法名调用方法
		MyMethod mm = new MyMethod();
		double r1 = mm.m4(10, 3.5);

		// 使用反射调用方法
		Method m = myclass.getMethod("m4", int.class, double.class);

		// 获取m4方法的一些信息
		System.out.println(m.getModifiers());
		System.out.println(m.getName());
		System.out.println(m.getParameterCount());
		System.out.println(Arrays.toString(m.getParameterTypes()));
		
		
		System.out.println(m.getReturnType());
		// 重点,使用反射执行方法invoke
		MyMethod obj = myclass.newInstance();
		Object r2 = m.invoke(obj, 3, 5);
		System.out.println(r2);

		Method m3 = myclass.getMethod("m3");
		MyMethod obj2 = myclass.newInstance();
		Object r3 = m3.invoke(obj2);
		System.out.println(r3);

	}
}

其中调用getModifiers是获取方法的权限,什么都不加 是0 , public  是1 ,private 是 2 ,protected 是 4,static 是 8 ,final 是 16。如果是public  static final  三个修饰的,就是3 个的加和 为 25 。

反射的练习:通过反射擦除掉ArrayList集合的泛型。

package com.reflect;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Work1 {
	//通过使用反射擦除泛型
	//泛型只是在编译阶段去判断要存入的对象是不是符合规定标准
	public static void main(String[] args) throws Exception, SecurityException {
		//创建存储数值类型为Integer类型的数组
		ArrayList<Integer> list=new ArrayList<>();
		//调用数组的add方法
		list.add(10);
		
		//首先获取到它类的类型
		Class arrayListClass=Class.forName("java.util.ArrayList");
//		Class<ArrayList> arrayListClass=ArrayList.class;
//		通过类的类型类型获取方法
		Method addMethod=arrayListClass.getMethod("add", Object.class);
//		使用invoke调用方法
		Object result =addMethod.invoke(list, "yuhang");
		System.out.println(result);
		System.out.println(list);
	}

}

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值