Java类加载机制:
JVM和类:
运行Java: java 带有main方法的类名
启动JVM,并加载字节码.
当调用java命令来运行某个Java程序时,该命令将会启动一个JVM进程.同一个JVM中的所有线程,变量都处于同一个进程中,共享该JVM的内存区域.
当出现以下情况是,JVM会退出:
1):程序正常执行结束.
2):使用System.exit(0)方法;
3):出现异常时,没有捕获异常.
4):平台强制结束JVM进程.
JVM进程一旦结束,该进程中内存中的数据将会丢失.
当程序主动使用到某个类时,如果该类还未被加载进内存中,则系统会通过加载,连接,初始化三个步骤来对该类进行初始化操作.
1):类的加载:
类加载时指将类的class文件(字节码文件)载入内存中,并为之创建一个java.lang.Class对象,我们称之为字节码对象.
类的加载过程由类加载器(ClassLoader)完成,类加载器通常有JVM提供,我们称之为系统类加载器,我们也可以继承ClassLoader类来提供自定义类加载器.
不同的类加载器可以实现加载本地字节码文件,jar包中的字节码,通过网络加载字节码等.
2):类的连接:
当类被加载进内存之后,系统为之生产一个对应的Class对象,接着把类的二进制数据合并到JRE中.
1>:验证:检测被加载的类是否有正确的内部结构.
2>:准备:负责为类的static变量分配内存,并设置默认值.
3>:解析:把类的二进制数据中的符号引用替换为直接引用(深入分析JVM).
3):类的初始化:
在此阶段,JVM负责对类进行初始化,主要就是对static变量进行初始化.
类的初始化一个类包含以下几个步骤:
1>:如果该类还未被加载和连接,则程序先加载并连接该类.
2>:如果该类的直接父类还未被初始化,则先初始化其父类.
3>:如果类中有初始化语句(静态代码块),则系统依次执行这些初始化语句.
符号引用:
符号引用是一个字符串,它给出了被引用的内容的名字并且可能会包含一些其他关于这个被引用项的信息——这些信息必须足以唯一的识别一个类、字段、方法。
这样,对于其他类的符号引用必须给出类的全名。
Class类和Class类的实例
Class类: 用来描述类或者接口的类型,描述类的类.
Class类的实例: 在JVM中的一份份字节码,Class实例表示在JVM中的类或者接口,枚举是一种特殊的类,注解是一种特殊的接口.
当程序第一次使用某一个java.util.Date类的时候,就会把该类的字节码对象加载进JVM,并创建出一个Class对象.
此时的Class对象就表示java.util.Date的字节码.
Class类可以表示N个类的字节码对象,问题,到底怎么区分Class类此时表示的那一个类的字节码呢?
为了解决该问题,Class类的设计者提供了泛型.--->Class
java.lang.String类的字节码类型: Class;
java.util.Date类的字节码类型: Class;
java.util.ArrayList类的字节码类型: Class;
获取类中的构造器
需求:通过反射来获取某一个类的构造器:
1):获取该类的字节码对象.
2):从该字节码对象中去找需要获取的构造器.
Class类获取构造器方法:
Constructor类:表示类中构造器的类型,Constructor的实例就是某一个类中的某一个构造器
public Constructor>[] getConstructors()
:该方法只能获取当前Class所表示类的public修饰的构造器
public Constructor>[] getDeclaredConstructors():
获取当前Class所表示类的所有的构造器,和访问权限无关
public Constructor
:获取当前Class所表示类中指定的一个public的构造器
参数:parameterTypes
表示:构造器参数的Class类型
如:
public User(String name)
Constructor c = clz.getConstructor(String.class);
public Constructor getDeclaredConstructor(Class>... parameterTypes) :获取当前Class所表示类中指定的一个的构造
使用反射调用构造器创建对象
构造器最大的作用:创建对象.
为什么使用反射创建对象,为什么不直接来new呢?
在框架中,提供给我们的都是字符串.
使用反射创建对象:
步骤:
1);找到构造器所在类的字节码对象.
2):获取构造器对象.
3):使用反射,创建对象
Constructor
常用方法:
public T newInstance(Object... initargs):如调用带参数的构造器,只能使用该方式.
参数:initargs:表示调用构造器的实际参数
返回:返回创建的实例,T表示Class所表示类的类型
如果:一个类中的构造器是外界可以直接访问,同时没有参数.,那么可以直接使用Class类中的newInstance方法创建对象.
public Object newInstance():相当于new 类名();
调用私有的构造器:
获取类中的方法
使用反射获取类中的方法:
1):获取方法所在类的字节码对象.
2):获取方法.
Class类中常用方法:
public Method[] getMethods():获取包括自身和继承过来的所有的public方法
public Method[] getDeclaredMethods():获取自身类中所有的方法(不包括继承的,和访问权限无关)
public Method getMethod(String methodName,
Class>... parameterTypes):表示调用指定的一个公共的方法(包括继承的)
参数:
methodName: 表示被调用方法的名字
parameterTypes:表示被调用方法的参数的Class类型如String.class
public Method getDeclaredMethod(String name,
Class>... parameterTypes):表示调用指定的一个本类中的方法(不包括继承的)
参数:
methodName: 表示被调用方法的名字
parameterTypes:表示被调用方法的参数的Class类型如String.class
使用反射调用方法:
1):获取方法所在类的字节码对象.
2):获取方法对象.
3):使用反射调用方法.
如何使用反射调用一个方法:
在Method类中有方法:
public Objectinvoke(Object obj,Object... args):表示调用当前Method所表示的方法
参数:
obj: 表示被调用方法底层所属对象
Method m = clz.getMethod("sayHi",String.class);
args:表示调用方法是传递的实际参数
返回:
底层方法的返回结果
调用私有方法:
在调用私有方法之前:应该设置该方法为可访问的
又因为Method是AccessibleObject子类,所以Method中具有该方法.
sayGoodByeMethod.setAccessible(true);
使用反射调用静态方法:
静态方法不属于任何对象,静态方法属于类本身.
此时把invoke方法的第一个参数设置为null即可.
使用反射调用数组参数(可变参数):
王道:调用方法的时候把实际参数统统作为Object数组的元素即可.
Method对象.invoke(方法底层所属对象,new Object[]{ 所有实参 });
操作反射其他API
反射其它的API:
Class类中:
int getModifiers():获得修饰符
String getName():返回类的全限定名
Package getPackage():获得该类的包
String getSimpleName():获得类的简单名字
Class getSuperclass():获得类的父类
boolean isArray():判断该Class实例是否是数组
boolean isEnum() :判断该Class实例是否是枚举
Constructor,Method,Filed的信息:
去查阅相应类的API即可.