运行时类型信息使得你能够在程序运行时发现和使用类型信息java
经过
Class
对象能够在运行时发现一个对象完整的类继承结构数组
类是程序的一部分,每个类都会有一个Class
对象。换句话说既每编写一个新的类,就会产生一个Class
对象。而这些Class
对象信息是保存在咱们用javac 类名.java
进行编译时产生的.class
文件中的。为了生成这个对象,运行这个程序的java虚拟机(JVM)会使用类加载器进行加载ide
Java类加载器(Java Classloader)是Java运行时环境(Java Runtime Environment)的一部分,负责动态加载Java类到Java虚拟机的内存空间中函数
即类加载器是Java虚拟机将描述类的数据,例如类的各类方法,构造参数之类的信息从Class
文件加载到内存中去,而且对数据进行校验、转换解析和初始化,最终造成能够被虚拟机直接使用的java类型。this
全部的类都是在对其进行第一次使用的时候,动态加载到JVM中的,即和编译时须要进行链接工做的语言不通,在java中,类型的加载、链接和初始化都是在程序运行期间完成的。code
类加载器在进行加载的时候会首先检查这个类的Class对象是否已经进行了加载,若是没有加载,那么默认的类加载器就会根据类名进行查找.class
文件。经过下面例子,咱们能够清楚看出类是在第一次被使用的时候才会加载。对象
static静态代码块的加载是在类加载的时候进行的继承
class Tom{ static { System.out.println("Loading Tom"); } } class Jerry{ static { System.out.println("Loading Jerry"); } } class Mary{ static { System.out.println("Loading Mary"); } } public class Demo4 { public static void main(String[] args) { System.out.println("Inside Main"); new Tom(); System.out.println("After Loading Tom"); new Mary(); System.out.println("After Loading Mary"); new Jerry(); } }
输出结果以下:接口
Inside Main Loading Tom After Loading Tom Loading Mary After Loading Mary Loading Jerry
类加载器在加载类的过程当中会分为三个阶段内存
.class
文件的字节码文件同一个类加载器下,一个类型只会初始化一次。
.getClass()
方法获取Tom tom = new Tom(); Class class = tom.getClass();
.class
获取Class class = Tom.class;
Class.forName
,其中的参数必须为带包名的类路径Class c = Class.forName("Tom");
一般在反射中建立Class对象时使用的第二种方法,由于第一种已经有这个对象了,干吗还须要反射,第三种的话会有局限性,须要导入所要建立对象的包路径。
咱们在上面获取到Class
对象,而咱们拿到了Class
对象之后就能对其进行操做
Class
类和javalang.reflect
类库一块儿对反射的概念进行了支持,该类库包含了Field
,Method
以及Constructor
类(每一个类都实现了Member接口).这些类型的对象是由JVM在运行时建立的,用以表示未知类里所对应的成员信息.这样就能够用Constructor
建立新的对象。下面演示一下经过反射获取构造方法。
public class A { private String a; private String b; public String c; public A(String a, String b) { this.a = a; this.b = b; } private A(String a){ this.a=a; } public A(){} ——————————get.set方法省略 }
Class a=Class.forName("Practice.Day05.A"); Constructor[] constructors = a.getConstructors(); for (Constructor constructor:constructors){ System.out.println(constructor); }
此时咱们发现打印出来的信息以下:
public Practice.Day05.A() public Practice.Day05.A(java.lang.String,java.lang.String)
咱们发现没有私有的构造函数,由于getConstructors()
方法得到是public
的构造函数,而getDeclaredFields()
方法得到是包括public
, protected
, default
,private
的构造函数,此时若是咱们将代码改为以下:
Class a=Class.forName("Practice.Day05.A"); Constructor[] constructors = a.getDeclaredConstructors(); for (Constructor constructor:constructors){ System.out.println(constructor); }
咱们发现打印的参数就会多了一个private的构造函数
public Practice.Day05.A() private Practice.Day05.A(java.lang.String) public Practice.Day05.A(java.lang.String,java.lang.String)
咱们在上面都是得到了一个构造方法的数组,若是想要得到具体的构造方法的话,那么能够经过传入构造方法的入参的类型,能够得到这个构造方法,具体例子以下:
Class a=Class.forName("Practice.Day05.A"); Constructor constructor = a.getConstructor(null); Constructor stringConstructor = a.getConstructor(String.class,String.class); Constructor stringPrivateConstructor=a.getDeclaredConstructor(String.class); System.out.println(constructor); System.out.println(stringConstructor); System.out.println(stringPrivateConstructor);
打印的信息以下:
public Practice.Day05.A() public Practice.Day05.A(java.lang.String,java.lang.String) private Practice.Day05.A(java.lang.String)
####2.1.4 经过构造方法实例化类
咱们得到了Constructor
对象之后,能够经过其中的newInstance
方法进行实例化对象。例子以下:
Class a=Class.forName("Practice.Day05.A"); Constructor constructor = a.getConstructor(null); Constructor stringConstructor = a.getConstructor(String.class,String.class); A nullA= (A) constructor.newInstance(); A stringA= (A) stringConstructor.newInstance("BuXueWuShu","BuXueWuShu"); nullA.setA("BuXueWuShu"); System.out.println("nullA:"+nullA.getA()); System.out.println("stringA:"+stringA.getA());
打印信息以下:
nullA:BuXueWuShu stringA:BuXueWuShu
仍是上面的实体类的例子,其中有私有的两个变量是a和b,私有的变量是c。
若是类中有属性的话,两个方法都是返回一个Field
数组.其中getDeclaredFields()
方法返回的是全部参数包括public
, protected
, default
,private
,而getFields()
只返回public
的参数。例如以下
Class a=Class.forName("Practice.Day05.A"); Field[] allDeclaredFields = a.getDeclaredFields();--得到全部成员变量 Field[] fields = a.getFields();--只得到public的成员变量 for (Field field:allDeclaredFields){ System.out.println(field); } System.out.println("-----------------------"); for (Field field:fields){ System.out.println(field); }
打印的参数以下:
private java.lang.String Practice.Day05.A.a private java.lang.String Practice.Day05.A.b public java.lang.String Practice.Day05.A.c ----------------------- public java.lang.String Practice.Day05.A.c
得到指定的成员变量其实和上面的获取指定的构造方法是同样的。举例以下:
Class a=Class.forName("Practice.Day05.A"); Field c1 = a.getField("c"); Field a1 = a.getDeclaredField("a"); System.out.println(c1); System.out.println("---------------------"); System.out.println(a1);
打印参数以下:
public java.lang.String Practice.Day05.A.c --------------------- private java.lang.String Practice.Day05.A.a
得到了Field的对象后,能够调用get()
和set()
方法对某个对象中的属性进行取值和赋值的操做。例子以下
Class a=Class.forName("Practice.Day05.A"); Constructor nullClass=a.getDeclaredConstructor(null); A nullA= (A) nullClass.newInstance();--得到A的实例化对象 Field a1 = a.getDeclaredField("a"); a1.setAccessible(true);--变量a是private的,因此须要解除私有限定 a1.set(nullA,"BuXueWuShu");--为nullA对象中的变量a进行赋值操做 System.out.println("nullA="+a1.get(nullA));--取出nullA对象中的变量a
打印信息以下:
nullA=BuXueWuShu
注意在对私有的成员变量进行赋值操做时,要解除私有限定,调用
setAccessible()
方法,赋值为true
经过得到的Class
对象,调用它的getDeclaredMethods()
和getMethods()
方法能够得到类中的方法的信息。例若有如下的一个类。
public class TestMethod { private String playName; public void show(String playName){ System.out.println("I Love "+playName); } private String returnPlayName(){ return playName; } }
作以下的调用:
Class a=Class.forName("Practice.Day05.TestMethod"); Method[] allDeclaredMethods = a.getDeclaredMethods(); Method[] methods = a.getMethods(); for (Method method:allDeclaredMethods){ System.out.println(method); } System.out.println("-----------------"); for (Method method:methods){ System.out.println(method); }
能够发现打印以下的信息:
public void Practice.Day05.TestMethod.show(java.lang.String) private java.lang.String Practice.Day05.TestMethod.returnPlayName() ----------------- public void Practice.Day05.TestMethod.show(java.lang.String) public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait() throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
getDeclaredMethods()
方法会得到自身和实现的接口中全部的方法,可是不会得到继承来的方法,getMethods()
方法会得到全部的不管是实现的接口仍是继承来的public
的方法
经过方法名和方法中的参数类型就能够得到指定的方法
Class a=Class.forName("Practice.Day05.TestMethod"); Method show = a.getDeclaredMethod("show", String.class); System.out.println(show);
打印信息以下:
public void Practice.Day05.TestMethod.show(java.lang.String)
能够经过调用Method
对象的invoke()
方法进行调用方法。例子以下:
Class a=Class.forName("Practice.Day05.TestMethod"); Constructor nullClass=a.getDeclaredConstructor(null); TestMethod nullTestMethod= (TestMethod) nullClass.newInstance(); Method show = a.getDeclaredMethod("show",String.class); show.invoke(nullTestMethod,"BasketBall");
打印参数以下:
I Love BasketBall
若是调用的是private的方法,那么在使用
invoke()
方法以前要先解除私有限定,即调用setAccessible()
方法,赋值为true