1、反射出现的背景意义
servlet中并没有主方法既是main方法,因此其每次运行都需要依靠tomcat.而tomcat是用Java写的,tomcat中有主方法,servlet和tomcat中的主方法一结合就可以启动运行了。因此我们可以这样认为只有有主方法的才能自己运行,可以作为一个进程运行。而没有主方法的不能自己运行的只能作为一个线程存在。
tomcat调用servlet运行,但tomcat是如何调用servlet的?servlet也是一个类,其中也能像普通类中定义除了doget(),dopost()外的各种方法。由于servlet定义的名字五花八门,tomcat不知道各个servlet的名字于是更不可能通过new的方式创建servlet对象,那对象中的doget(),dopost()方法也是不能被调用的。
各种编程语言一般都有扫描文件的能力,但即使知道文件路径,那你是如何确定那个.class文件是servlet呢?servlet和普通类一样名字都是可以任意取的。况且也没有相应的语法知道了类文件的
因此依据之前的方式,tomcat是无法获得servlet对象的,更无法调用servlet中的各种方法
为了解决这个问题出现了反射
2、类的生命周期
3、反射机制的作用
1、在运行时判断任意一个对象所属的类
2、在运行时构造任意一个类的对象
3、在运行时的到一个类所具有的成员变量和方法
4、在运行时调用任何一个类所具有的成员变量和方法
5、生成动态代理
4、反射相关的主要类
1、java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象。
2、java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
3、java.lang.reflect.Field:代表类的成员变量,Field代表某个类的成员变量
4、java.lang.reflect.Constructor:代表类的构造方法,Constructor表示某个类的构造器
5、Class类的细节
1、Class也是类,因此也继承Object类
2、Class类对象不是new出来的,而是系统创建的。Class类是通过类加载器ClassLoader生成的ClassLoader中有一个loadClass()方法。通过该方法生成某个类的类对象。
- 传统new 对象的方式创建对象
会进入该方法中然后返回类对象
- 反射方式创建对象仍会进入上图所示的方法
3、对于某个类的Class类对象,在内存中只有一份,因为类只加载一次。创建类对象时会加锁,防止多线程环境下生成多个Class类对象
public class Test {
public static void main(String[] args) throws Exception {
Cat cat=new Cat();
Class catClass=Class.forName("com.company.Cat");
}
}
此时若在Class catClass=Class.forName(“com.company.Cat”);处打断点执行的话,就不能进入loadClass方法了,因此Cat类已经在Cat cat=new Cat()语句中执行过一次了,类对象已经加载入内存中了。
4、每个类的实例都会记得自己是由那个Class实例所产生的
5、通过Class对象可以完整的得到一个类的完整结构,通过一系列的API
6、Class对象是存放在堆里的
7、类的字节码二进制数据是放在方法区的,有的地方称为类的元数据。
6、Class类的方法
public class Test {
public static void main(String[] args) throws Exception {
Class<?> catClass=Class.forName("com.company.Cat");
System.out.println(catClass);//显示catClass是那个类的Class对象 运行结果:class com.company.Cat
System.out.println(catClass.getClass());//class java.lang.Class
//2、得到包名
System.out.println(catClass.getPackage().getName());//com.company
//3、得到全类名
System.out.println(catClass.getName());//com.company.Cat
//4、通过类对象创建对象实例
Cat cat=(Cat)catClass.newInstance();
//5、通过反射得到对象的属性(只能获取公有属性)
Field name=catClass.getField("name");
System.out.println(name.get(cat));//发财
//6、通过反射给对象的属性赋值
name.set(cat,"招财");
System.out.println(name.get(cat));//招财
//7、得到对象的所有属性
Field[] fields=catClass.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
}
}
7、获取Class类对象的几种方式
方式一:Class.forName(“类的全路径”)
前提: 已知道一个类的全类名,且该类在类路径下,可以通过Class类的静态方法forName()获取
应用场景: 多用于配置文件,读取类的全路径,然后加载类
实例:
Class<?> catClass=Class.forName("com.company.Cat");
方式二:类名.class
前提: 已经知道具体的类,通过类名.class获取
方式三:对象.getClass()
前提: 有具体的对象实例
8、通过反射获取类的结构信息
获取关于类的相关信息
//第一组Api
//得到Class对象
Class personClass=Class.forName("com.company.Person");
//getName:获取全类名
System.out.println(personClass.getName());
//getSimpleName():获取简单类名
System.out.println(personClass.getSimpleName());
//getFields()获取所有public修饰的属性,包括本类以及父类的
Field[] fields=personClass.getFields();
for (Field field : fields) {
System.out.println("本类即父类的public修饰的属性"+field.getName());
}
//获取本类的所有属性,包括私有受保护的
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本类的所有的属性"+declaredField.getName());
}
//获取本类以及所有父类public的所有方法
Method[] methods=personClass.getMethods();
for (Method method : methods) {
System.out.println("本类以及所有父类public的所有方法"+method.getName());
}
//获取本类的所有方法
Method[] declaredMethods = personClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类的所有方法"+declaredMethod.getName());
}
//获取所有public修饰的构造器仅包括本类
Constructor[] constructors = personClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("获取所有public修饰的构造器仅包括本类"+constructor.getName());
}
//获取本类所有的构造器包括私有的
Constructor[] declaredConstructors = personClass.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println("本类所有的构造器"+declaredConstructor.getName());
}
//以Class的形式放回父类信息
Class superClass=personClass.getSuperclass();
System.out.println("父类的Class对象="+superClass);
//以Class[]的形式返回接口信息
Class[] interfaces=personClass.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println("接口信息"+anInterface.getName());
}
Annotation[] annotations=personClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("类上的注解信息"+annotation);
}
获取关于类的成员变量的具体信息
//第二组api获取关于类的成员变量的具体信息
//getModifiers:以int的形式返回修饰符
//默认修饰符的值为0 public时1 private是2 protected是4 static是8 final是16
//如果有多个修饰符返回修饰符的int值的和
//getType()获取属性的类型
Field[] declaredFields1 = personClass.getDeclaredFields();
for (Field field : declaredFields1) {
System.out.println("本类中的所有属性"+field.getName()
+"该属性的修饰符"+field.getModifiers()
+"该属性的类型"+field.getType());
}
获取关于方法的相关信息
//第三组Api关于类的方法
//getModifiers:以int的形式返回修饰符
//默认修饰符的值为0 public时1 private是2 protected是4 static是8 final是16
//getReturnType:以Class形式获取返回类型
//getName:放回方法名
//getAnnotations()获取方法上的注解信息
//getParameterTypes:以Class[]返回方法的参数类型数组
Method[] declaredMethods1 = personClass.getDeclaredMethods();
for (Method method : declaredMethods1) {
System.out.print("本类中的所有方法"+method.getName()
+" 该方法的修饰符"+method.getModifiers()
+" 该方法的返回类型"+method.getReturnType());
//获得该方法上的注解信息
Annotation[] annotations1 = method.getAnnotations();
for (Annotation annotation : annotations1) {
System.out.print(" 该方法上的注解"+annotation);
}
//输出当前这个方法的形参数组
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.print(" 该方法的形参类型"+parameterType);
}
System.out.println("");
}
9、通过反射创建类的实例
User类
class User{
public int age=20;
private String name="la";
public static Boolean sex;
public User(String name) {
this.name = name;
}
public User() {
}
private User(int age, String name) {
this.age = age;
this.name = name;
}
public void sayHi(){
System.out.println("sayHi");
}
public String la(){
return "mmmm";
}
private void sayHiHi(String name){
System.out.println(name+" private sayHiHi");
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class ReflectCreateInstance {
public static void main(String[] args) throws Exception {
//1、先获取类的Class对象
Class userClass =Class.forName("com.company.User");
//2、通过public的无参构造方法创建实例
Object o=userClass.newInstance();
System.out.println(o);
//3、通过public有参构造器创建实例
Constructor constructor=userClass.getConstructor(String.class);
Object o1=constructor.newInstance("wu");
System.out.println(o1);
//4、通过非public构造器创建实例
//得到private的构造器对象
Constructor constructor1=userClass.getDeclaredConstructor(int.class,String.class);
//但不能直接使用private的构造器对象创建实例,需要暴力破解
constructor1.setAccessible(true);//不设置执行下面的创建实例的方法会报错
Object o2=constructor1.newInstance(100,"god");
System.out.println(o2);
}
}
10、通过反射操作类的成员变量
//通过反射操作类的成员
Field age=userClass.getField("age");
age.set(o,88);//通过反射设置属性
System.out.println(age.get(o));
//使用反射操作private属性
Field name=userClass.getDeclaredField("name");
//暴力反射
name.setAccessible(true);
name.set(o,"hi");
System.out.println(name.get(o));
//public static Boolean sex;
//对于静态变量由于静态变量是属于类的。所以通过反射设置属性时。
//对象可以为空
Field sex=userClass.getDeclaredField("sex");
sex.set(null,true);
System.out.println(sex.get(null));
11、通过反射访问类中的方法
//使用反射获取类的方法
Method sayHi=userClass.getMethod("sayHi");
sayHi.invoke(o);
//使用反射调用私有方法
Method sayHiHi=userClass.getDeclaredMethod("sayHiHi",String.class);
//暴力反射
sayHiHi.setAccessible(true);
sayHiHi.invoke(o,"hi");
//在反射中如果方法中有返回值,统一返回Object
Method la=userClass.getMethod("la");
Object invoke=la.invoke(o);
System.out.println(invoke);