1.反射机制入门
1.1反射机制原理:![](https://i-blog.csdnimg.cn/blog_migrate/595d683d041afc9e0ce216ca44ea114d.png)
1.2JAVA反射机制可以完成:
1.在运行时判断任意一个对象所属的对象
2.在运行时构造一个类的对象
3.在运行时得到任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的成员变量和方法
5.生成动态代理
反射相关的主要类:
1.java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
2.java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
3.java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
getFiled()不能得到私有属性
4.java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
实例:
import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;
public class Reflection01 {
public static void main(String[] args) throws Exception {
//1.使用Properties类,读写文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath=properties.get("classfullpath").toString();
String method=properties.get("method").toString();
System.out.println("classfullpath="+classfullpath);
System.out.println("method"+method);
//2.使用反射机制解决问题
//(1)加载类,返回Class类型的对象cls
Class cls = Class.forName(classfullpath);
//(2)通过cls得到类的对象的实例
Object o = cls.newInstance();
System.out.println("o的运行类型="+o.getClass());
//(3)通过cls得到加载类的com.reflection.Cat的method"hi"的方法对象
//在反射中,可以把方法视为对象
Method method1 = cls.getMethod(method);
//通过method1调用方法,即通过方法对象来实现调用方法
method1.invoke(o);//传统方法 对象.方法() 反射机制 方法.invoke(对象) 符合开闭原则
//java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
//Field name = cls.getField("name"); 不能获取私有属性
Field age = cls.getField("age");
System.out.println(age.get(o));//传统写法 对象.成员变量 反射:成员变量.get(对象)
//java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
Constructor constructor = cls.getConstructor();
System.out.println(constructor);
//()里面可以指定构造器的参数类型,返回有参构造器 没有东西则返回无参构造器
Constructor constructor1 = cls.getConstructor(String.class);//String.class就是String类的class对象
System.out.println(constructor1);
}
}
1.3反射机制的优点与缺点
优点:可以动态的创建和使用对象,使用灵活,没有反射机制框架技术就会失去底部支撑
缺点:使用反射机制是执行解释,对执行的速度有影响
反射调用优化--关闭访问检查
1.Method和Field、Constructor对象都有setAccessible()方法
2.setAccessible作用是启动禁用访问安全检查的开关
3.参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false表示反射的对象执行访问检查
代码:
import java.lang.reflect.Method;
public class Reflection02 {
public static void main(String[] args) throws Exception {
m1();
m2();
m3();
}
//传统方法调用hi()方法
public static void m1() {
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("传统方法耗时="+(end-start));
}
//反射机制调用方法
public static void m2() throws Exception {
Class aClass = Class.forName("com.reflection.Cat");
Object o = aClass.newInstance();
Method hi = aClass.getMethod("hi");
long start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射机制调用方法耗时="+(end-start));
}
//反射调用优化+关闭访问检查
public static void m3() throws Exception {
Class aClass = Class.forName("com.reflection.Cat");
Object o = aClass.newInstance();
Method hi = aClass.getMethod("hi");
hi.setAccessible(true);//在反射调用方法时,取消访问检查
long start = System.currentTimeMillis();
for (int i = 0; i < 99999999; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("反射机制优化后调用方法耗时="+(end-start));
}
}
2.Class类分析
2.1.基本介绍
1.Class也是类,因此也继承Object类
2.Class类不是new出来的,而是由系统创建的
3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
4.每个类的实例都会记得自己是由哪个Class实例生成
5.通过Class可以完整的得到一个类的完整结构,通过一系列的API
6.Class类对象时存放在堆里面的
7.类的字节码二进制数据放在方法区,有的地方称为类的元数据(包括类的方法代码,变量,方法名,访问权限等等)
2.2Class类的常用方法
演示:
import java.lang.reflect.Field;
public class Class02 {
public static void main(String[] args) throws Exception {
String classFullPath="com.class_.Person";
//1.获取Person对应的Class类对象
Class<?> pClass = Class.forName(classFullPath);
//2.显示cls对象是哪个类的Class对象 "com.class.Person"
System.out.println(pClass);
System.out.println(pClass.getClass());//输出pClass的运行类型 java.lang.Class
//3.得到包名
System.out.println(pClass.getPackage().getName());//com.class_
//4.得到全类名
System.out.println(pClass.getName());//com.class_.Person
//5.通过反射创建实例
Person person =(Person)pClass.newInstance();
System.out.println(person);//Person{name='小张', age=12, gender='nan'}
//6.通过反射获取属性
Field name = pClass.getField("name");
System.out.println(name.get(person));//小张
//7.通过反射给属性字段赋值
name.set(person,"小王");
System.out.println(name.get(person));//小王
//8.获得所有的属性
Field[] fields = pClass.getFields();
for (Field f: fields) {
System.out.println(f.getName());
}
}
}
2.3获取Class类对象的6种方法
演示:
public class Class03 {
public static void main(String[] args) throws ClassNotFoundException {
//1.通过Class.forName
String classFullPath="com.class_.Person";//可以通过读取配置文件获取
Class<?> cls1 = Class.forName(classFullPath);
System.out.println(cls1);
//2.类名.class 应用场景:用于参数传递
//()里面可以指定构造器的参数类型,返回有参构造器 没有东西则返回无参构造器
//如:Constructor constructor1 = cls.getConstructor(String.class);//String.class就是String类的class对象
Class<Person> cls2 = Person.class;
System.out.println(cls2);
//3.对象.getClass() 多用于有对象实例
Person person = new Person();
Class<? extends Person> cls3 = person.getClass();
System.out.println(cls3);
//4.通过类加载器[4种]来获取类的Class对象
//(1)先获得类加载器personLoader
ClassLoader personLoader = person.getClass().getClassLoader();
//(2)通过类加载器得到Class对象
Class<?> cls4 = personLoader.loadClass(classFullPath);
System.out.println(cls4);
//5.基本数据类型(int,char,boolean,float,double,byte,long,short)按下面的方法得到Class类对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Double> doubleClass = double.class;
System.out.println(integerClass);//int
//6.基本数据类型对应的包装类,可以通过.TYPE得到Class类对象
Class<Integer> type = Integer.TYPE;
Class<Double> type1 = Double.TYPE;
System.out.println(type);//int
}
}
2.4如下类型有Class类对象
1.外部类,成员接口,静态内部类,局部内部类,匿名内部类
2.interface:接口
3.数组
4.enum:枚举
5.annotation:注释
6.基本数据类型
7.void
演示:
import java.io.Serializable;
public class AllTypeClass {
public static void main(String[] args) {
Class<String> cls1 = String.class;//外部类
Class<Serializable> cls2 = Serializable.class;//接口
Class<Integer[]> cls3 = Integer[].class;//数组
Class<float[][]> cls4 = float[][].class;//二维数组
Class<Deprecated> cls5 = Deprecated.class;//注解
Class<Thread.State> cls6 = Thread.State.class;//枚举
Class<Long> cls7 = long.class;//基本数据类型
Class<Void> cls8 = void.class;//void
Class<Class> cls9 = Class.class;
}
}
3.类加载
3.1静态加载与动态加载
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
1.静态加载:编译时加载相关的类,如果没有就会报错,依赖性太强
2.动态加载:运行时加载需要的类,如果运行时没有用到该类,则不报错,降低依赖性
类加载的时机:
1.当创建对象时(new) ->静态加载
2.当子类被加载时 ->静态加载
3.调用类中的静态成员时 ->静态加载
4.通过反射 ->动态加载
实例:
3.2类加载流程图
类加载的三个主要阶段
●加载阶段:JVM在该阶段主要目的是将字节码从不同的数据源(可能是class文件,也可能是jar包,甚至是网络)转化为二进制字节流加载到内存当中,并生成一个代表该类的java.lang.Class对象
●连接阶段
①验证:
1.目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
2.包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
3.可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间
②准备:JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始化值,如0,0L,null,false等),这些变量所使用的内存都将在方法区中进行分配
//1.n1是实例属性,不是静态变量,因此在准备阶段不会分配内存
//2.n2是静态变量,分配内存n2是默认初始化0,而不是20
//3.n3是static final为常量,与静态变量不一样,因为一旦赋值就不变 n3=30
public int n1=10;
public static int n2=20;
public static final int n3=30;
③解析:虚拟机将常量池内的符号引用替换为直接引用的过程
●初始化阶段
1.到初始化阶段才真正开始执行类中定义的Java程序,此阶段是执行<clinit>()方法的过程;
2.<clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和·静态代码块中的语句,并进行合并。
3.虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
3.3获取类结构信息
通过反射获取类的结构信息
演示实例:
package com.reflection;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionUtils {
public static void main(String[] args) {
}
//第一组API
@Test
public void m1() throws ClassNotFoundException {
//得到Class对象
Class<?> personCls = Class.forName("com.reflection.Person");
//1.getName:获取全类名
System.out.println(personCls.getName());
//2.getSimpleName:获取简单类名
System.out.println(personCls.getSimpleName());
//3.getFields:获取所有public修饰的属性,包含本类以及超类的
Field[] fields = personCls.getFields();
for (int i = 0; i < fields.length; i++) {
System.out.println("本类以及父类的public属性:"+fields[i].getName());
}
//4.getDeclaredFields:获取本类的所有属性
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField: declaredFields) {
System.out.println("本类的所有属性:"+declaredField.getName());
}
//5.getMethods:获取本类以及父类所有的public方法
Method[] methods = personCls.getMethods();
for (Method method: methods) {
System.out.println("本类以及父类的public方法:"+method.getName());
}
//6.getDeclaredMethods:获取本类所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod: declaredMethods) {
System.out.println("本类所有的方法"+declaredMethod.getName());
}
//7.getConstructors:获取本类的所有public构造器
Constructor<?>[] constructors = personCls.getConstructors();
for (Constructor constructor: constructors) {
System.out.println("本类所有public构造器"+constructor.getName());
}
//8.getDeclaredConstructors:获取本类所有的构造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor deconstructor:declaredConstructors) {
System.out.println("本类所有的构造器:"+deconstructor.getName());
}
//9.getPackage:以Package的方式返回包的信息
System.out.println("包的信息:"+personCls.getPackage());
//10.getSuperClass:以Class形式返回父类信息
System.out.println("父类信息:"+personCls.getSuperclass());
//11.getInterfaces:以Class[]形式返回接口信息
Class<?>[] interfaces = personCls.getInterfaces();
for (Class ainterface:interfaces) {
System.out.println("接口信息:"+ainterface);
}
//12.getAnnotations:以Annotation[]形式返回注解信息
Annotation[] annotations = personCls.getAnnotations();
for (Annotation annotation:annotations) {
System.out.println("注解信息:"+annotation);
}
}
@Test
public void m2() throws ClassNotFoundException {
//得到Class对象
Class<?> personCls = Class.forName("com.reflection.Person");
//getDeclaredFields:获取本类的所有属性
//规定说明 :默认修饰符0 public 1 private 2 protected 4
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField: declaredFields) {
System.out.println("本类的所有属性:"+declaredField.getName()+",该属性的修饰符值="+declaredField.getModifiers()
+",该属性的类型:"+declaredField.getType());
}
//getDeclaredMethods:获取本类所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod: declaredMethods) {
System.out.println("本类所有的方法"+declaredMethod.getName()+",该方法的访问修饰符值="+declaredMethod.getModifiers()
+",该方法的返回值类型="+declaredMethod.getReturnType());
//输出当前方法的形参数组
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class parameterType:parameterTypes) {
System.out.println("该方法的形参类型="+parameterType);
}
}
//getDeclaredConstructors:获取本类所有的构造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor deconstructor:declaredConstructors) {
System.out.println("本类所有的构造器:"+deconstructor.getName());
Class[] parameterTypes = deconstructor.getParameterTypes();
for (Class parameterType:parameterTypes) {
System.out.println("该构造器的形参类型="+parameterType);
}
}
}
}
class A {
public String aa;
protected int bb;
String cc;
private double dd;
public A() {
}
private A(String aa) {
this.aa = aa;
}
public void b1(){
}
protected void b2() {
}
void b3() {
}
private void b4() {
}
}
interface IA{}
interface IB{}
@Deprecated
class Person extends A implements IA,IB{
public String name;
protected int age;
String job;
private double sal;
public Person() {
}
private Person(String name) {
this.name = name;
}
public void a1(String name,int age,double sal){
}
protected void a2() {
}
void a3() {
}
private void a4() {
}
}
3.4反射爆破
1.通过反射创建对象
- 方法一:调用类中public修饰的无参构造器
- 方法二:调用类中指定构造器
- Class类相关方法
①newInstance:调用类中的无参构造器,获取对应类的对象
②getConstructor( Class ):根据参数列表,获取对应的构造器对象
③getDeclaredConstructor( Class ):根据参数列表,获取对应的构造器对象
- Constructor类相关方法
①setAccessible:爆破
②newInstance(Object ):调用构造器
实例:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.先获取User的Class对象
Class<?> userCls = Class.forName("com.reflection.User");
//2.通过public无参构造器创建实例
Object o = userCls.newInstance();
System.out.println(o);
//3.通过public有参构造器创建实例
/*
constructor对象即
public User(String name, int age) {
this.name = name;
this.age = age;
}
*/
Constructor<?> constructor = userCls.getConstructor(String.class,int.class);
Object xiaoMing = constructor.newInstance("小明", 12);
System.out.println(xiaoMing);
//4.通过非public有参构造器创建实例
Constructor<?> declaredConstructor = userCls.getDeclaredConstructor(String.class);
//爆破:暴力破解 ,使用反射可以访问private构造器/方法/属性
declaredConstructor.setAccessible(true);
Object xiaoHong = declaredConstructor.newInstance("小红");
System.out.println(xiaoHong);
}
}
class User{
private String name;
private int age;
public User() {
}
private User(String name) {
this.name = name;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.通过反射操作类中的属性
- 根据属性名过去Field对象 Field f=Class对象.getDeclaredField(属性名)
- 爆破:f.setAccessible(true)
- 访问 f.set(o.值) f.get(o)
- 如果是静态属性,则set和get中的参数o,可以写成null
实例:
import java.lang.reflect.Field;
public class ReflectAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.获取Class对象
Class<?> stuCls = Class.forName("com.reflection.Student");
//2.创建对象
Object o = stuCls.newInstance();
System.out.println(o.getClass());
//3.使用反射得到属性
Field name = stuCls.getField("name");
name.set(o,"Jane");
System.out.println(o);
System.out.println(name.get(o));
//4.操作私有静态属性
Field age = stuCls.getDeclaredField("age");
age.setAccessible(true);
//age.set(o,12);
age.set(null,12);//因为age是static属性,o也可以写成null
System.out.println(o);
System.out.println(age.get(null));
}
}
class Student{
public String name;
private static int age;
public Student() {
}
private Student(String name) {
this.name = name;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3.通过反射操作类中的方法
- 根据方法名和参数列表获取Method方法对象 Method m=Class.getDeclaredMethod
- 获取对象 newInstance
- 爆破 setAccessble
- 访问:m.invoke(o)
- 注意:如果是静态方法,invoke里面的参数可以写作o
实例:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.获取Class对象
Class<?> teaCls = Class.forName("com.reflection.Teacher");
//2.创建对象
Object o = teaCls.newInstance();
//3.调用public方法
Method hi = teaCls.getMethod("hi",String.class);
hi.invoke(o,"hi");
//4.调用private static方法
Method hello = teaCls.getDeclaredMethod("hello", String.class);
hello.setAccessible(true);//爆破
hello.invoke(o,"你好");
//hello.invoke(null,"你好~");
//5.在反射当中,如果有返回值,统一返回Object 但是运行类型和方法定义的放回类型一样
Object method = hi.invoke(o, "你们hi");
System.out.println(method.getClass());
}
}
class Teacher{
public String name;
private static int age;
public Teacher() {
}
private Teacher(String name) {
this.name = name;
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String hi(String s) {
System.out.println("hi");
return s;
}
private static void hello(String s) {
System.out.println("hello");
}
}