一、理解Class类并获取Class实例
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
- 反射就是把java类中的各种成分映射成一个个的Java对象:
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
1、关于java. Lang.Class类的理解
1、类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.cLass结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为CLass的一个实例。
2、换句话说,Class的实例就对应着一个运行时类。
Class c1 = 运行时类
3、加载到内存中的运行时类,会缓存一定的时间,在此时间内,我们可以通过不同的方式来获取此运行时类
2、获取Class的实例方式:
方式一:调用运行时类的属性:.class
方法二:调用运行时类的对象,调用getClass()方法
方式三:调用Class的静态方法:forName(String classPath)(常用)
(注意此字符串必须是真实路径,就是带包名的类路径,包名.类名)
方法四:使用类的加载器:ClassLoader
//获取Class的实例方法
@Test
public void test1() throws ClassNotFoundException {
//方法一:调用运行时类的属性:.class
Class c1= Person.class;
System.out.println(c1);
//方法二:调用运行时类的对象,调用getClass()方法
Person p1=new Person();
Class c2=p1.getClass();
System.out.println(c2);
//方式三:调用Class的静态方法:forName(String classPath)
Class c3= Class.forName("D1.Person");//(需要指明那个包下的类)
System.out.println(c3);
//方法四:使用类的加载器:ClassLoader
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class<?> c4 = classLoader.loadClass("D1.Person");
System.out.println(c4);
System.out.println(c1==c4);
System.out.println(c1==c2);
System.out.println(c2==c3);
}
class D1.Person
Person()
class D1.Person
class D1.Person
class D1.Person
true
true
true
二、创建运行时类的对象
newInstance():调用此方法,创建对应的运行时类的对象,内部调用了运行时类的空参构造器
//通过反射创建运行时类的对象
@Test
public void test1() throws InstantiationException, IllegalAccessException {
Class clazz = Person.class;
Object obj = clazz.newInstance();
Person p=(Person)clazz.newInstance();
//newInstance():调用此方法,创建对应的运行时类的对象,内部调用了运行时类的空参构造器
System.out.println(obj);
System.out.println(p);
}
Person()
Person()
Person{age=0, name=‘null’}
Person{age=0, name=‘null’}
三、调用运行时类的完整结构(属性、方法、构造器等)
1、获取当前运行时类的属性结构(Field)
方法一:getFields():获取当前运行时类及其父类中声明为public访问权限的属性
方法二:getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
public void test1(){
Class clazz=Person.class;
//获取属性结构
//getFields():获取当前运行时类及其父类中声明为public访问权限的属性
Field[] fields = clazz.getFields();
for(Field f:fields){
System.out.println(f);
}
System.out.println("------");
//getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f:declaredFields){
System.out.println(f);
}
}
2、获取当前运行时类的方法结构(Method)
方法一:getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
方法二:getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
@Test
public void test1(){
Class clazz= Person.class;
//getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
Method[] methods = clazz.getMethods();
for(Method m: methods){
System.out.println(m);
}
System.out.println("---------");
//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
Method[] declaredMethods = clazz.getDeclaredMethods();
for(Method m:declaredMethods){
System.out.println(m);
}
}
3、获取构造器结构
方法一:getConstructors():获取当前运行时类中声明为public的构造器
方法二:getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
4、获取其他结构
1、获取运行时类的父类
getSuperclass()方法
2、获取运行时类的带泛型的父类
getGenericSuperclass()方法
3、获取运行时类的带泛型的父类的泛型
//获取运行时类的带泛型的父类的泛型(以下代码会用就行,知道干什么的)(会用)
@Test
public void test3(){
Class clazz= Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType=(ParameterizedType) genericSuperclass;
//获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(actualTypeArguments[0].getTypeName());
}
4、获取运行时类实现的接口(会用)
getInterfaces()方法
5、获取当前运行时类所在的包
getPackage()方法
6、获取当前运行时类声明的注释
getAnnotations()方法
四、调用运行时类的指定结构:属性、方法、构造器(主要是方法和属性)
1、调用运行时类中指定的属性
在已有Class实例和运行时类对象的基础上
step1:getDeclaredField(String fieldName):
获取运行时类中指定变量名的属性
step2:保证当前属性是可访问的(不管什么访问权限的属性,都加上下句)获取属性名.setAccessible(true);
step3:根据需求获取、设置指定对象的此属性值
//非常重要:如何操作运行时类中的指定的属性-----必须掌握
@Test
//开发中用如下方法
public void testField1() throws Exception {
Class clazz= Person.class;
//创建运行时类对象
Person p= (Person) clazz.newInstance();
//1、getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2、保证当前属性是可访问的(不管什么访问权限的属性,都加上下句)
name.setAccessible(true);
//3、根据需求获取、设置指定对象的此属性值
name.set(p,"cxc");
System.out.println(name.get(p));
}
2、调用运行时类中指定的方法
在已有Class实例和运行时类对象的基础上
step1:获取指定的某个方法:
getDeclaredMethod():参数1:指明获取方法的名称,参数2:指明获取的方法的形参列表(因为同名的方法很多,根据形参区分具体哪一个方法)
step2:保证当前的方法是可访问的:调用的方法名.setAccessible(true);
step3:调用方法的invoke():
参数1:方法的调用者,参数2:给方法形参赋值的实参,invoke()的返回值即为对应类中调用的方法的返回值
@Test
public void testMethod() throws Exception {
Class clazz= Person.class;
//创建运行时类对象
Person p= (Person) clazz.newInstance();
/*
1、获取指定的某个方法
getDeclaredMethod():参数1:指明获取方法的名称,参数2:指明获取的方法的形参列表(因为同名的方法很多,根据形参区分具体哪一个方法)
*/
Method show = clazz.getDeclaredMethod("show", String.class);
//2、保证当前的方法是可访问的
show.setAccessible(true);
/*
3、调用方法的invoke():参数1:方法的调用者,参数2:给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值
*/
show.invoke(p,"CHN");//String nation =p.show("CHN“)
Object returnValue = show.invoke(p,"CHN");
System.out.println(returnValue);
System.out.println("-------如何调用静态方法--------");
//private static void showDesc()
Method showDesc = clazz.getDeclaredMethod("showDesc");
//如果调用的运行时类中的方法没有返回值,则此invoke()返回null
showDesc.setAccessible(true);
// Object returnVal =showDesc.invoke(Person.class);
Object returnVal =showDesc.invoke(null);//(这两种方法都可以)
System.out.println(returnVal);
}
}