下面这张图(分三个阶段)说明了源码怎么变成我们的实例对象的。
阶段一 :源码 通过javac编译 ,变成.class(也称字节码文件)->
阶段二:.class通过类加载器(classloader ,对于我们来说,我们一般都是通过class.forname()来加载) 变成 class对象 ->
阶段三:class对象 通过newInstance实例化成特定的 实例化对象
class.forname和classloader的区别
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
Class.forName得到的class是已经初始化完成的,Classloder.loaderClass得到的class是还没有链接的。
而对于反射,主要是阶段二、三,下面我们来介绍什么是反射。
1.什么是反射机制
Java-Reflection(JAVA反射)是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够通过Java-Reflection来调用它的任意方法和属性(不管是公共的还是私有的)。
这种动态获取信息以及动态调用对象方法的行为被称为java的反射机制。
2.反射机制有什么用
通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
通过上面这张图片,我们用代码来调用他们。
不过在进行代码之前,需要讲解一下, 源代码阶段就是 .java
那么源码如何变成class类(字节码)呢,有三种方法。
获取反射中的Class对象
在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。
在 Java API 中,获取 Class 类对象有三种方法:
第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");
第二种,使用 .class 方法。
这种方法只适合在编译前就知道操作的 Class。
Class clz = String.class;
第三种,使用类对象的 getClass() 方法。
String str = new String("");
Class clz = str.getClass();
通过反射创建类对象
通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。
第一种:通过 Class 对象的 newInstance() 方法。
Class clz = Apple.class;
Apple apple = (Apple)clz.newInstance();
第二种:通过 Constructor 对象的 newInstance() 方法
Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();
通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。
Class clz = Apple.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
Apple apple = (Apple)constructor.newInstance("红富士", 15);
注:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。
否则会抛出java.lang.InstantiationException
异常。
package com.example.aoptest.controller;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class money {
private int possess;
private String name;
public int getPossess() {
return possess;
}
public void setPossess(int possess) {
this.possess = possess;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public money(){
}
public money(int possess, String name) {
this.possess = possess;
this.name = name;
}
public void hh(){
System.out.println("我是哈哈哈");
}
public static void main(String[] args) throws Exception {
//正常的实例化对象
money m = new money();
m.setName("哈哈");
//使用反射实例化对象
Class<?> aClass = Class.forName("com.example.aoptest.controller.money");
System.out.println("我是成员变量");
//普通的反射获取属性
Field[] fields = aClass.getFields();
//通过暴力反射,才能获取私有属性
Field name1 = aClass.getDeclaredField("name");
System.out.println(name1);
//获取所有的属性
Field[] declaredFields = aClass.getDeclaredFields();
for (Field f:declaredFields
) {
System.out.println(f.getName());
}
//如何调用方法method
System.out.println("我是成员方法");
// 大概过程就是
// 1.先用 Class对象获取指定的方法,如果该方法中有参数,需指定参数类型
//2.通过 Class对象获取构造器,可以选着有参构造和无参构造 .
// 3. 通过构造器 new Instance 实例化一个对象 .
//4. 如果想要调用方法 ,就用 方法.invoke(实例化的具体对象,该调用方法的参数,如果没参数可以不写).
//比如hh.invoke(o)。
Method setPossess = aClass.getMethod("setPossess", int.class);
Method getPossess = aClass.getMethod("getPossess");
Method hh = aClass.getMethod("hh");
Constructor<?> constructor1 = aClass.getConstructor(int.class,String.class);
Object o = constructor1.newInstance(7,"gg");
System.out.println(setPossess.invoke(o, 5));
System.out.println(getPossess.invoke(o));
System.out.println(hh.invoke(o));
/* Method[] methods = aClass.getMethods();
for (Method me:methods ) {
System.out.println(me);
}
*/
System.out.println("-------- 注解 ");
// 具体实例.class 返回的是class<T> a,b,d,e,T 等等其实都是泛型的意思,只是用于区分而已。
MyAnnotation annotation = aClass.getAnnotation(MyAnnotation.class);
System.out.println(annotation);
System.out.println(annotation.name());
System.out.println(annotation.value());
System.out.println(annotation.annotationType());
}
}
总之,要获取私用的,都是通过getDeclared。 如果有参数的,就得把参数类型写上,无论是方法还是构造方法,如果是构造方法写的是有参构造,那么获得的构造器实例化时也需要把具体参数值写上,其实就跟我们平时 money m = new money(“参数1”,“参数2”); 一样。
对于一个类,还有类的方法,属性,相信通过上面,我们都知道如何去获取了。那么 如果一个类,或者,或者一个属性上使用了注解,那么我们怎么获取呢?
我们先自定义一个注解, 叫做MyAnnotation ,给注解定义两个属性,name和value
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
public String name();
public String value();
}
在money类上,我们使用注解,并给name和value随便赋值。
通过getAnnotation(注解.Class)来获取注解
MyAnnotation annotation = aClass.getAnnotation(MyAnnotation.class); 获得 MyAnnotation这个注解类
@MyAnnotation(name="namehh",value = "hhhvalue")
public class money {
public static void main(String[] args) throws Exception {
System.out.println("-------- 注解 ");
// 具体实例.class 返回的是class<T> a,b,d,e,T 等等其实都是泛型的意思,只是用于区分而已。
MyAnnotation annotation = aClass.getAnnotation(MyAnnotation.class);
System.out.println(annotation);
System.out.println(annotation.name());
System.out.println(annotation.value());
System.out.println(annotation.annotationType());
/* 下面是上面代码的输出
-------- 注解
@com.example.aoptest.controller.MyAnnotation(name="namehh", value="hhhvalue")
namehh
hhhvalue
interface com.example.aoptest.controller.MyAnnotation */
}
}
同样的,如果你想获得所有public注解,依然是通过
Annotation[] annotations = aClass.getAnnotations();
for (Annotation a:annotations ) {
System.out.println(a);
}
反射主要使用的种类
类名 用途
Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量(成员变量也称为类的属性)
Method类 代表类的方法
Constructor类 代表类的构造方法
Annotation类 代表类的注解
具体每个东西拥有的方法,我在这里不列出来了,百度一大堆。比如
Method代表类的方法。
方法 | 用途 |
---|---|
invoke(Object obj, Object... args) | 传递object对象及参数调用该对象对应的方法 |
getParameterTypes() | 获取方法所有的参数类型 |
getReturnType() | 获取方法返回对象的类型 |
getParameterTypes() | 返回方法中参数对象的Class类型 |
getGenericParameterTypes() | 返回方法中参数对象的Type类型 |
getAnnotation(Class<T> annotationClass) | 获取方法上面的注解 |
isAnnotationPresent(Class<T> annotationClass) | 判断是否对应的注解 |