JAVA:反射(参考书籍:《JAVA编程的逻辑》)

反射

类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过 **加载,连接,初始化 **三步来实现对这个类进行初始化。

加载:就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象。

连接:验证,是否有正确的内部结构,并和其他类协调一致。准备,负责为类的静态成员分配内存,并设置默认初始化值。解析,将类的二进制数据中的符号引用替换为直接引用。

初始化

类初始化时机

  1. 创建类的实例

  2. 类的静态变量,或者为静态变量赋值

  3. 类的静态方法

  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

  5. 初始化某个类的子类

  6. 直接使用java.exe命令来运行某个主类

反射

反射是在运行时,而非编译时,动态获取类型的信息,比如接口信息,成员信息,方法信息,构造方法信息,再根据这些动态获取到的信息来创建对象,访问、修改成员,使用方法。 ------- 《JAVA编程的逻辑》

Class类

Class没有公共的构造方法。注意Class类不是class,大小写要区分。这里的前两种获取Class类对象的方法需要明确Person类型。而第三种需要指定这种类型的字符串就行。

//通过Object类中的getClass()方法 , 返回Object类的运行时类
Person p = new Person();
Class c = p.getClass();
//通过 类名.class 获取到字节码文件对象
Class c2 = Person.class;
//将类名作为字符串传递给Class类中的静态方法forName
Class c3 = Class.forName("Person");

1、Class是个泛型类,有一个类型参数,getClass()并不知道具体的类型,只能返回 Class<?>

2、接口也有Class对象。

3、**基本类型 **没有getClass()方法,但都有对应的Class对象。void 作为特殊的返回类型,也有Class对象。

//基本类型。这里就不列举完了,还有Double,Character等
Class<Integer> inCls = int.class;
Class<Byte> byteCls = byte.class;
。。。。
//void类型
Class<void> voidCls = void.class;

4、对于数组,每种类型的数组都有对应的Class对象,每个 **维度 **都有一个。

获取构造方法并使用

在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。

其中,构造方法使用类 Constructor 表示。

//获取public修饰, 指定参数类型所对应的构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
//获取指定参数类型所对应的构造方法(包含私有的)
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//获取所有的public 修饰的构造方法
public Constructor<?>[] getConstructors()
//获取所有的构造方法(包含私有的)
public Constructor<?>[] getDeclaredConstructors()
    
    
    
    
    
public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
		//获取Class对象
		Class c = Class.forName("cn.itcast_01_Reflect.Person");//包名.类名
		
		//获取所有的构造方法
         //这里不止有一个构造方法,所以运用集合来存储
		Constructor[] cons = c.getDeclaredConstructors();
		//用循环来打印方法
        for (Constructor con : cons) {
			System.out.println(con);
		}
		
		System.out.println("------------------------");
		//获取一个构造方法,这里getConstructor的参数就是指定构造方法的参数。
		//public Person() 
		Constructor con1 = c.getConstructor(null);
		System.out.println(con1);
		
		//public Person(String name)
		Constructor con2 = c.getConstructor(String.class);
		System.out.println(con2);
		
		//private Person(String name, int age)
		Constructor con3 = c.getDeclaredConstructor(String.class, int.class);
		System.out.println(con3);
		
		//public Person(String name, int age, String address)
		Constructor con4 = c.getDeclaredConstructor(String.class, int.class, String.class);
		System.out.println(con4);
	}
}

获取构造方法,创建对象

获取 **构造方法 **,步骤如下:

  1. 获取到Class对象。
  2. 获取指定的构造方法。
  3. 通过构造方法类 Constructor 中的方法,创建对象。

获取私有构造方法,创建对象

获取 私有构造方法 ,步骤如下:

  1. 获取到Class对象。
  2. 获取指定的构造方法。
  3. 暴力访问, 通过 setAccessible(boolean flag)方法,这个方法可以忽略JAVA的访问权限。
  4. 通过构造方法类 Constructor 中的方法,创建对象。

AccessibleObject 类是 Field、Method 和 Constructor 对象的 父类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力,也就是可以打破JAVA的访问权限。

//参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。
public void setAccessible(boolean flag) throws SecurityException

获取成员变量并使用

在反射机制中,把类中的成员变量使用类 Field 表示。

//获取指定的 public修饰的变量
public Field getField(String name)
//获取指定的任意变量
public Field getDeclaredField(String name)
//获取所有public 修饰的变量
public Field[] getFields()
//获取所有的 变量 (包含私有)
public Field[] getDeclaredFields()
    
    
    
    
public class FieldDemo {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
		//获取Class对象
		Class c = Class.forName("cn.itcast_01_Reflect.Person");
		
		//获取成员变量
		//多个变量,运用数组来存储
		Field[] fields =  c.getDeclaredFields();
         //遍历数组,访问数组
		for (Field field : fields) {
			System.out.println(field);
		}
		System.out.println("-----------------");
		//一个变量
		//public int age;
		Field ageField = c.getField("age");
		System.out.println(ageField);
		
		//private String address,这里是私有变量,也能被获取。
		Field addressField = c.getDeclaredField("address");
		System.out.println(addressField);
	}
}

成员变量进行赋值与获取值操作

获取成员变量,步骤如下:

  1. 获取Class对象

  2. 获取构造方法

  3. 通过构造方法,创建对象

  4. 获取指定的成员变量(私有成员变量,通过setAccessible(boolean flag)方法暴力访问)

//在指定对象obj中,将此 Field 对象表示的成员变量设置为指定的新值
public void set(Object obj, Object value)
//返回指定对象obj中,获取对象的值
public Object get(Object obj)
    
    
  	     //1,获取Class对象
		Class c = Class.forName("cn.itcast_01_Reflect.Person");
		//2,获取构造方法
		//public Person(String name, int age, String address)
		//这里要传入对应构造方法的参数类型的class对象!!!!!!!!!
		Constructor con = c.getConstructor(String.class, int.class, String.class);
		//3,通过构造方法,创建对象
		Object obj = con.newInstance("小明", 23, "哈尔滨");
		//4,获取指定的方法
		//public void method1()  没有返回值没有参数的方法
		//Method m1 = c.getMethod("method1", null);

		//public String method4(String name)
		Method m4 = c.getMethod("method4", String.class);
		
		//5,执行找到的方法
		//m1.invoke(obj, null);
		
		Object result = m4.invoke(obj, "itcast");

获取成员方法并使用

在反射机制中,把类中的成员方法使用类 Method 表示。

//获取 public 修饰的方法
public Method getMethod(String name, Class<?>... parameterTypes)
//获取任意的方法,包含私有的
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
   
    
//这里因为返回不只一个方法,因此运用Method的数组来存放。类似前面的。
//获取本类与父类中所有public 修饰的方法
public Method[] getMethods()
//获取本类中所有的方法(包含私有的)
public Method[] getDeclaredMethods()

创建对象调用指定的方法

//执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定
public Object invoke(Object obj,  Object... args)

创建对象调用指定的 private 方法

获取私有成员方法,步骤如下:

  1. 获取Class对象
  2. 获取构造方法
  3. 通过构造方法,创建对象
  4. 获取指定的方法
  5. 开启暴力访问
  6. 执行找到的方法

也就是运用 setAccessible(true); 开启暴力访问。

总结

1、获取.Class文件对象的方法三种。

2、获取构造方法。

3、获取成员变量。

4、获取成员方法。

5、暴力破除JAVA语言局限,setAccessible (true)。

猜你喜欢

转载自blog.csdn.net/weixin_41960890/article/details/84642415