java 反射使用

前言

这里简单学习java反射的使用,后面会结合注解再一起在进行学习,后面几篇文章会集合retrofft一起讲解,主要是学习retrofft的思想以及使用到的Java技术,如有纰漏之处,万望指出。

反射的定义

反射的定义:在运行的状态中,对于一个实体类,都能知道这个类的所有属性和方法;对于任意对象,都能够调用任意方法和属性,这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

理解虚拟机加载过程

java程序经过javac编译器编译,生产.class字节码文件,.class字节码文件是一个二进制文件,包含了java虚拟机指令集和符号表以及若干其他辅助信息。这是一个虚拟机能识别的加载的文件。
而反射获取的数据就是经过虚拟机加载后生产的Class文件中的数据,这里的Class文件和编译后生成的.class文件并不是同一个文件,要理解这个我们得先知道虚拟机的类加载机制。

虚拟机加载过程

加载–>连接(–>验证–>准备–>解析)–>初始化–>使用–>卸载

加载、验证、准备、初始化的顺序是一定的,但是类加载过程的第一个阶段加载并没有约束在什么时候必须进行 ,但类型的加载、连接和初始化都是在运行期完成的, 而在初始化阶段遇到5种情况必须对类进行初始化

加载:通过一个类的全限定名来获取定义此类的二进制字节流,并将这个字节流所代表的静态结构转换成方法区运行的数据结构,并在堆中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。java反射获取的就是这个Class对象中的数据。也就是说在使用反射的时候,是需要首先获取到这个类的二进制字节流。

验证:主要做4个验证动作1、文件格式验证、元数据验证、字节码验证和符号引用验证。
准备:这是正式为类变量(static修饰的变量)分配内存并设置类变量初始值的阶段,
解析:虚拟机将常量池内的符号引用替换成直接引用的过程,主要动作针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号。

初始化:初始化阶段是执行类构造器()方法的过程。()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块()中的语句构成的。在初始化的过程中,首先初始化的是父类的()方法,因此虚拟机中第一个执行的()方法的一定是java.lang.Object

剩下的就是使用和卸载,详细了解请查看《深入理解java虚拟机》

动态加载

我们知道设计模式中有一种设计模式叫动态代理,动态代理技术就是使用了反射,而使用反射加载类的方式我们称为动态加载。当然,不是说仅有反射是动态加载。区分动态加载和静态加载是类被加载的时机

  • 静态加载:比如在java源码中使用new Animal(),那么这样就具体的知道当前方法需要加载的类,属于静态加载。编译器在编译的时候会根据new 关键字去检查Animal类是否存在,不存在就给与提示
  • 动态加载:如果在某个类中调用反射Class.forName(“com.reflection.Animal”)获取一个Animal; 那么在我们使用命令行编译这个类的时候,是不会报错的,只有在运行期间才会进行初始化的时候,检查到com.reflection.Animal下没有该类才会报错

反射支持类

这里我知道反射获取的就是加载后生成的Class对象中的数据,那么获取到这些信息需要哪些核心类的支持呢

  • Class类:代表一个类
  • Field类:代表一个类的成员变量
  • Method类:代表类的成员方法
  • Constructor类:代表类的构造方式
  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法
    Class类
    首先我们看下java.lang包下的部分Class类源码,Class类封装一个对象和接口运行时的状态,当虚拟机加载类时,Class类型的对象自动创建。Class没有公共构造方法。Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的,因此不能显示地声明一个Class对对象。
public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
    private static final int ANNOTATION= 0x00002000;
    private static final int ENUM      = 0x00004000;
    private static final int SYNTHETIC = 0x00001000;
    private static native void registerNatives();
    static {
        registerNatives();
    }
    /*
     * Private constructor. Only the Java Virtual Machine creates Class objects.
     * This constructor is not used and prevents the default constructor being
     * generated.
     */
    private Class(ClassLoader loader) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
    }
. . . 
}

Class类的常用方法

修饰语和类型名称描述
StringgetName()返回String形式的该类的名称
Class<? super T>getSuperClass()返回某子类所对应的直接父类所对应的Class对象
booleanisArray()判定此Class对象所对应的是否是一个数组对象
ConstructorgetConstructor(Class[])返回当前 Class 对象表示的类的指定的公有构造子对象
Constructor<?>[]getConstructors()返回当前 Class 对象表示的类的所有公有构造子对象数组。
ConstructorgetDeclaredConstructor​(Class<?>… parameterTypes)返回一个Constructor对象,该对象反映此Class对象表示的类或接口的指定构造 函数
Constructor<?>[]getDeclaredConstructors​()返回一个Constructor对象数组,反映由此Class对象表示的类声明的所有构造 函数
FieldgetDeclaredField​(String name)返回一个Field对象,该对象反映此Class 对象表示的类或接口的指定声明字段。
Field[]getDeclaredFields​()返回一个Field对象数组,反映由此Class对象表示的类或接口声明的所有字段 。
FieldgetField​(String name)返回一个Field对象,该对象反映此Class 对象表示的类或接口的指定公共成员字段。
Field[]getFields​()返回一个数组,其中包含Field反映此Class对象所表示的类或接口的所有可访问公共字段的对象。
MethodgetDeclaredMethod​(String name, Class<?>… parameterTypes)返回一个Method对象,该对象反映此Class对象表示的类或接口的指定声明方法
Method[]getDeclaredMethods​()返回一个数组,其中包含Method反映此 Class对象所表示的类或接口的所有声明方法的对象,包括public,protected,default(包)访问和私有方法,但不包括继承的方法。
MethodgetMethod​(String name, Class<?>… parameterTypes)返回一个Method对象,该对象反映此Class对象表示的类或接口的指定公共成员方法 。
Method[]getMethods​()返回一个数组,其中包含Method反映此 Class对象所表示的类或接口的所有公共方法的对象,包括由类或接口声明的那些以及从超类和超接口继承的那些方法。

使用核心类提供方法

public class Animal {
 	private String name;
 	private int age;
 	private String msg = "hello wrold";
 	public String getName() {
 		return name;
 	}
 	public void setName(String name) {
 		this.name = name;
 	}
 	public int getAge() {
 		return age;
 	}
 	public void setAge(int age) {
 		this.age = age;
 	}
 	public Animal() {
 	}
 	private Animal(String name) {
 		this.name = name;
 		System.out.println("我是私有构造方法传入一个String参数:" + name);
 	}
 	public void fun() {
 		System.out.println("fun");
 	}
 	public void fun(String name, int age) {
 		System.out.println("我叫" + name + ",今年" + age + "岁");
 	}
 	private void run() {
 		System.out.println("我是私有方法");
 	}
}
public class Test {
 	public static void main(String[] args) throws Exception {
 		String a = "abc";
 		Class c1 = a.getClass();
 		Animal animal = new Animal();
 		Class c2 = animal.getClass();
 		Class c3 = c2.getSuperclass();
 		//根据类的全限定名加载class
 		Class c4 = Class.forName("com.reflection.Animal");
 		Class c9 = Animal.class;


 		System.out.println(c2);
 		System.out.println(c4);
 		System.out.println(c9);
 		System.out.println(c9.getModifiers());
 		Class c5 = String.class;
 		Class c6 = int.class;
 		Class c7 = boolean.class;// 还有其他基本数据类型class可以获取
 		Class c8 = void.class;
    

 		// Constructor 类:代表类的构造方法。
 		// Field 类:代表类的成员变量(成员变量也称为类的属性)。
 		// Method类:代表类的方法。
 		System.out.println("==========调用Animal的公共方法fun");
 		Object o = c4.newInstance();
 		Method m = c4.getMethod("fun", String.class, int.class);
 		m.invoke(o, "tian", 10);

 		System.out.println("==========调用Animal的私有方法run");
 		Method m1 = c4.getDeclaredMethod("run");
 		m1.setAccessible(true);
 		System.out.println("方法名称:" + m1.getName());
 		System.out.println("方法类型:" + m1.getModifiers());
 		m1.invoke(o);

 		System.out.println("==========获取Animal所有的public方法,包括父类的");
 		Method[] m2 = c4.getMethods();// 得到该类所有的public方法,包括父类的
 		for (Method med : m2) {
 			String methodName = med.getName();
 			System.out.println("++" + methodName);
 		}
 		System.out.println("==========获取Animal所有的方法,不包括父类的");
 		Method[] methods = c4.getDeclaredMethods(); // 得到该类所有的方法,不包括父类的
 		for (Method med : methods) {
 			String methodName = med.getName();
 			System.out.println("==" + methodName);
 		}


 		Field field = c4.getDeclaredField("msg");
 		Object o2 = c4.newInstance();
 		field.setAccessible(true);// 设置是否允许访问,因为该变量是private的,所以要手动设置允许访问,如果msg是public的就不需要这行了。
 		Object msg = field.get(o2);
 		System.out.println("==========获取Animal的msg属性");
 		System.out.println("" + msg);
 		System.out.println("==========获取Animal所有所属");
 		Field[] fiel = c4.getDeclaredFields(); 获取所有成员变量的数组:
 		for (Field field2 : fiel) {
 			field2.setAccessible(true);
 			Object msgs = field2.get(o2);
 			System.out.println("所有属性" + msgs);
 		}
 		System.out.println("==========调用Animal的所有的构造器,不包括其父类的构造器 (构造方法必须声明传递参数)");
 		Constructor constructor = c4.getDeclaredConstructor(String.class);// 访问一个有String
 																			// 参数的构造方法
 		constructor.setAccessible(true);// 设置是否允许访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不需要这行了。
 		constructor.newInstance("tian");// 传入一个参数值为 tian
 		System.out.println("==========调用Animal的所有的构造器,包括其父类的构造器");
 		Constructor[] con = c4.getDeclaredConstructors();
 		for (Constructor cons : con) {
 			System.out.println(cons);
 		}

 		System.out.println("==========反射对泛型的理解");
 		List list = new ArrayList<>();
 		List<String> list2 = new ArrayList<String>();
 		list2.add("长度");
 		System.out.println(list2.size());
 		Class cl1 = list.getClass();
 		Class cl2 = list2.getClass();
 		System.out.println(list == list2); // 结果:true,说明类类型完全相同
 		Method mlist = cl2.getMethod("add", Object.class);
 		mlist.invoke(list2, 10);
 		System.out.println("list2长度:" + list2.size());
 	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值