第15-02章:理解Class类并获取Class实例
我的后端学习大纲
我的Java学习大纲
1、Java反射机制原理图:
- 源代码通过
Javac
编译得到字节码文件,当我执行到new一个对象的时候,字节码文件会通过ClassLoader被加载,然后得到一个Class类对象,存放在堆中,加载完后Cat对象就生成了,生成的这个对象一定知道它是属于哪个Class类对象的
2、理解java.lang.Class类:
1.1.类的加载过程:
a.编译:
- 程序经过
javac.exe
命令以后,会编译生成一个或多个字节码文件(.class结尾)
b.类加载:
- 1.我们使用
java.exe
命令对某个字节码文件进行解释运行,相当于把某个字节码文件加载到内存中。这个过程就是类加载的过程(不包括编译那一步
) - 2,加载到内存中的类,我们就称为
运行时类
,这个运行时类,就作为Class类的一个实例对象
- 比如
Class clazz = Cat.class
,Cat的这个运行时类:Cat.class
就是Class类的实例对象
- 换句话说,
Class的实例对象就对应着一个运行时类
。类的本质也是对象,万事万物皆对象
- 加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过6种不同的方式来获取此运行时类(
获取Class类的实例对象
)
- 比如
3、获取Class类实例对象的方法:
根据下图类加载的过程,我们
在不同阶段有不同方式来获取Class类实例对象
3.1.方式1:在代码阶段:
通过Class的静态方法
forName(String classPath)
获取
- 1.已知一个类的全类名,且该类在类的路径下,那么就可以通过Class类的静态方法
forName()
获取,可以抛出ClassNotFoundExpection
- 2.实例对象:
Class cls1 = Class.forName("java.lang.Cat")
,这种方式会进行类的静态初始化工作
- 3.
应用场景
:多用于配置文件,读取类全路径,加载类
//方式一:调用Class的静态方法:forName(String classPath),参数是类的包路径
Class clazz3 = Class.forName("com.atguigu.java.Person");
//clazz3 = Class.forName("java.lang.String");
System.out.println(clazz3);//输出com.atguigu.java.Person
3.2.方式2:在加载阶段:
在加载阶段,通过调用运行时类的属性
.class
获取
- 1.若已经知道具体的类,通过类的class获取,此方式最为安全可靠,程序性能最高,举例如
Class cls = Cat.class;
这种方式不会做任何类的初始化工作 - 2.应用场景:
多用于参数传递
、如通过反射得到对应的构造器对象。
//方式二:调用运行时类的属性.class
Class clazz1 = Person.class;
System.out.println(clazz1);//输出com.atguigu.java.Person
3.3.方式3:在运行阶段
在运行阶段,通过调用运行时类的对象,通过
对象.getClass()
获取
- 1.已
知某个类的实例
,调用该实例的getClass()方法
获取Class对象实例; - 2.实例:Class cls = 对象.getClass();//运行类型
- 3.应用场景:通过创建好的对象,获取Class对象;
- 4.这种方式:
静态初始化和非静态初始化工作都会进行
//方式三:通过运行时类的对象,调用getClass(),已经知道了Person类的P对象,通过调用这个类的getclass方法就可以获取Class类的实例
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println(clazz2);//输出com.atguigu.java.Person
3.4.方式4:使用类加载器:ClassLoader()
通过
类加载(4种加载器)
器获取Class类对象:
- 1.
ClassLoader cls = 对象.getClass().getClassLoader();class clazz4 = cls.loadClass("全类名")
//方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classLoader = car.getClass.getClassLoader();//得到类加载器car
Class clazz4 = classLoader.loadClass("com.atguigu.java.");//通过类加载器得到Class对象
System.out.println(clazz4);
3.5.方式5:基本数据类型获取Class类对象:
- 1.基本数据类型
(int\char\boolean\float\double\byte\long\short)
按照如下方式,可以获取到Class类对象Class cls = 基本数据类型.class
3.6.方式6:基本数据类型对应的包装类:
- 1.基本数据类型对应的包装类,可以通过
.type
得到Class类对象;Class cls = 包装类.type
//5. 基本数据(int, char,boolean,float,double,byte,long,short) 按如下方式得到Class类对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println(integerClass);//int
//6. 基本数据类型对应的包装类,可以通过 .TYPE 得到Class类对象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE; //其它包装类BOOLEAN, DOUBLE, LONG,BYTE等待
System.out.println(type1);
//下面两个输出的值是一样的
System.out.println(integerClass.hashCode());
System.out.println(type1.hashCode());
4、获取Class类的实例对象
4.1.哪些类型可以作为Class类的实例对象
- 1.
外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- 2.
interface
:接口 - 3.
enum:
枚举 - 4.
annotation
:注解 - 6.
基本数据类型
- 7.
void
- 8.
Class本身
4.2.编码测试:
package com.hspedu.reflection.class_;
import java.io.Serializable;
/**
* 演示哪些类型有Class对象
*/
public class AllTypeClass {
public static void main(String[] args) {
Class<String> cls1 = String.class;//外部类
Class<Serializable> cls2 = Serializable.class;//接口
Class<Integer[]> cls3 = Integer[].class;//数组
Class<float[][]> cls4 = float[][].class;//二维数组
Class<Deprecated> cls5 = Deprecated.class;//注解
Class<Class> cls6 = Class.class;//注解
//枚举
Class<Thread.State> cls6 = Thread.State.class;
Class<Long> cls7 = long.class;//基本数据类型
Class<Void> cls8 = void.class;//void数据类型
Class<Class> cls9 = Class.class;//
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
System.out.println(cls5);
System.out.println(cls6);
System.out.println(cls7);
System.out.println(cls8);
System.out.println(cls9);
}
}
1.2.理解java.lang.Class类:
- 1.
Class也是类
,因此也继承Object类
- 在Object类中定义了方法:
public final Class getClass()
,这个方法将被所有子类继承。 public final Class getClass()
这个方法返回值的类型是一个Class类
,此类是Java反射的源头,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。- 实际上所谓反射从程序的运行结果来看也很好理解,即:
可以通过对象反射求出类的名称
- 在Object类中定义了方法:
- 2.
Class类对象
不是new出来的,只能是系统创建出来的
;- 通过debugger发现,反射也是通过ClassLoad类加载Class对象
- 通过debugger发现,反射也是通过ClassLoad类加载Class对象
- 3.对于某个类的Class类对象,在
堆内存
中只有一份,因为类只加载一次
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 一个Class对象对应的是一个加载到JVM中的一个.class文件
- 4.
每个类的实例对象都会记得自己是由哪个Class实例对象(运行时类)所生成的
- 5.通过一系列的API和Class类的实例对象可以完整的得到一个类的完整结构:
- 6.
Class对象是存放在堆中的
,类的字节码二进制数据,是存放在方法区的
,有的地方称之为类的元数据
(包括:方法代码,变量名,方法名,访问权限等)