Java——反射篇

反射

类的加载
类初始化时机
反射
Class类的使用
动态加载类
方法的反射
获取构造方法并使用
获取构造方法创建对象
获取私有构造方法创建对象
获取成员变量并使用
成员变量进行赋值和获取值操作
获取成员方法并使用
创建对象调用指定的private方法

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

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

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

初始化:类进行初始化操作。

  • 类的初始化时机:

创建类的实例
类的静态变量,或者为静态变量赋值
类的静态方法
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类

  • 反射:
    (定义)在运行时,而非编译时,动态获取类型的信息,比如接口信息,成员信息,方法信息,构造方法信息,再根据这些动态获取到的信息来创建对象,访问、修改成员,使用方法。

  • Class类:
    Class没有公共的构造方法,所有的类都是它的实例类

public class ClassDemo {
    public static void main (String[] args){
        Person person=new Person();
    }
}
class Person{}

对于上述示例代码,person是Person类的实例对象,Person类是Class类的一个实例对象。但是Class类的实例对象不是new出来的。

3种获取Class类的实例对象的方式:

//方式1:在知道类名的情况下,调用该类的隐式的静态成员变量class
Class c1=Person.class;
//方式2:在知道实例对象名的情况下调用对象的getClass()方法
Class c2=person.getClass();

**上述的c1、c2表示了Person类的类类型。且上述2种方式需要明确Person类型。**方式1通过“类名.class”方式获取到字节码文件对象。方式2通过Object类中的getClass()方法,返回Object类的运行时类。

方式3:我们在JDBC连接MySQL连接驱动的时候会看到Class.forName(…).通过Class类的forName()方法创建,但是需要捕获异常。

//方式3:Class.forName()方式创建
Class c3=null;
try{
	c3=Class.forName("Person");
}catch(ClassNotFoundException e){
	e.printStackTrace();
}

针对以上的3种方式创建的对象,我们可输出一下判断他们是否完全相等。

System.out.println(c1 == c2 && c2 == c3);//true

结果:
Java——反射篇_第1张图片
当然,我们也完全可以通过类的类类型创建该类的实例对象,调用类类型的newInstance()方法(需要做类型强转和捕获异常)

public class ClassDemo {
    public static void main (String[] args){
        Person person=new Person();
        Class c1 =  Person.class;
        Class c2 = person.getClass();
        Class c3 = null;
        try {
            c3=Class.forName("Person");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(c1==c2&& c2==c3);

        //通过类的类类型创建该类的实例对象,调用类类型的newInstance()方法
        try {
            Person person1= (Person) c1.newInstance();
            person1.show();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}
class Person{
    public void show(){
        System.out.println("通过类类型也可以创建实例对象!");
    }
}

上述代码运行后能正常打印。
Class类还提供了很多获取成员信息的方法(成员方法、变量、构造方法),可以查阅文档。

Class 是个泛型类,有一个类型参数,getClass()并不知道具体的类型,只能返回Class.(Java中?表示不确定,Kotlin中,用*表示不确定)

接口也有Class 对象。

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

 Class intCls=int.class;
 Class byteCls=byte.class;
  //基本类型还有Double,Character等....
  //特殊的返回类型Void:
 Class voidCls=void.class;

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

  • 动态加载类:
    new关键字:创建对象属于静态加载。在编译时就要加载所有的类。若一个有问题就会影响整个程序。
//不使用动态加载
if("Person".equals(args[0])){
	Person person = new Person();
}
if("You".equals(args[0])){
	You you =new You();
}

上述代码中的**args[0]是一个命令行参数,动态传递进来的。**上述代码要求这2个类都得存在,如果有一个不存在,即使用不到这个类,也会影响整个程序。所以下方介绍的动态加载很好的解决这一问题:

**动态加载:**在调用的时候再去加载类。

try{
	Class class = Class.forName(args[0]);
	Person person= (Person)class.newInstance();
}

此处并不知道传递进来的是什么类型的,在强转的时候,在创建所有要用到的类(比如You类实现Person接口)的时候统一实现Person接口即可(泛型)。于是只有当用到才会调用此类。

  • 方法的反射:

下面的代码为简单的反射:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ClassDemo {
    public static void main (String[] args){
        Person person=new Person();
        Class c =person.getClass();
        try {
//            Method method=c.getMethod("show",new Class[]{int.class,int.class});
            Method method=c.getMethod("show",int.class,int.class);
            method.invoke(person,2,3);
            Method method1=c.getMethod("show");
            method1.invoke(person);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
class Person{
//    public void show(){
//        System.out.println("通过类类型也可以创建实例对象!");
//    }
    public void show(){
        System.out.println("无参方法");
    }
    public void show(int a, int b){
        System.out.println(a+b);
    }
    
}
  1. 通过前面提到的3种方式获取到类的类类型,这里就是c;
  2. 调用getMethod()方法获取到类的方法,参数1为方法名,参数2可以是一个参数数组,也可以直接把参数全部写出来,没有参数就可以不写或者写空数组。
  3. 使用方法对象的invoke()方法来调用该类的方法,第一个参数是类的实例对象,参数2、3和getMethod()写法相同。
  4. 运行程序,可以看见2个show()方法被执行了。
  • 获取构造方法并使用:
    反射机制中,把类中的成员(包括构造方法、成员变量、成员方法)都封装成了对应的类进行表示。
    其中,构造方法使用类Constructor表示:
//获取public修饰, 指定参数类型所对应的构造方法
public Constructor getConstructor(Class... parameterTypes)
//获取指定参数类型所对应的构造方法(包含私有的)
public Constructor 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); 开启暴力访问。

你可能感兴趣的:(JavaSE)