JVM和类
当调用java命令运行某一个Java程序时,该命令会启动一个JAVA虚拟机进程,同一个JVM的所有线程、所有变量都在同一个进程里,它们都使用JVM进程的内存区。当系统出现以下几种情况的时候,JVM进程将被终止:
1)程序运行到最后正常结束
2)程序运行到使用System.exit()或Runtime.getRuntime().exit()代码处结束程序
3)程序运行过程中遇到未捕获的异常或者错误而结束
4)程序所在的平台强制结束了JVM进程
当程序主动使用某个类的时候,如果该类还没有被加载到内存中,那么系统会通过加载、连接、初始化三个步骤对该类进行初始化。
类的加载
类加载是指将类的class文件读入内存,并为之创建一个java.lang.Class对象。类的加载由类加载器完成,类加载器通常由JVM提供,类加载器通常无需等到首次使用该类的时候才加载该类,JAVA虚拟机规范允许系统预先加载某些类。
类的连接
连接阶段负责把类的二进制数据合并到JRE中,类连接又分为三个阶段:
1)验证:验证阶段用于检验被加载的类是否具有正确的内部结构
2)准备:类的准备阶段负责为类的类变量分配内存
3)解析:将类的二进制的符号引用替换成直接引用
类的初始化
在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对类变量进行初始化。
JVM初始化一个类的步骤:
1)假如一个类还没有被加载和连接,那么先加载和连接这个类
2)加入一个类的直接父类没有被初始化,那么先初始化这个父类
3)假如类中有初始化语句,那么系统依次执行这些初始化语句。
类初始化的时机
1)创建类的实例
2)调用某个类的类方法
3)访问某个类或者接口的类变量,或者为该类赋值
4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5)初始化某个类的子类
6)直接使用java.exe命令来运行某个主类
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。例如许多JAVA程序在运行时会出现两种类型:编译时类型和运行时类型,比如Person p=new Student();由于在编译时无法预知该对象属于哪个类,程序只能依靠运行时的信息来发现该类对象和类的真实信息,这就需要用到反射。
Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
Class类
前面说过,类加载的时候会创建一个java.lang.Class对象,存在堆内存中。通过Class中的方法可以得到一个类中的完整结构,包括此类中的构造器、方法定义,属性定义等。
在Class类中没有任何的构造方法,如果要使用必须通过Class.forName(包名.类名)的静态方法实例化对象,也可以使用类.class或者对象.getClass()方法实例化。
Person类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
通过无参构造实例化对象
如果想要通过Class类本身实例化其他类的方法,可以用newInstance()方法,但是必须要保证实例化的类中存在一个无参构造方法。
public class Client {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class<?> a=Class.forName("reflect.Person");
Person p=(Person)a.newInstance();
p.setAge(18);
System.out.println(p);
}
}
通过Class.forName()方法实例化Class对象之后,直接调用newInstance()方法就可以根据传入的包名+类名进行实例化操作。
通过有参构造实例化对象
1)通过Class类的getConstructors取得本类中的全部构造方法
2)然后使用constructor对象的newInstance方法获得对象实例
public class Constructor1 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class<?> a=Class.forName("reflect.Person");
Constructor<?>[] cons=a.getConstructors();
Person p=(Person)cons[1].newInstance("张三",18);
System.out.println(p);
}
}
取得类的结构
Constructor:表示类的构造方法
Field:表示类中的属性
Method:表示类的方法
public class Client {
public static void main(String[] args) throws ClassNotFoundException {
Class a=Class.forName("reflect.Person");
System.out.println(a.getClassLoader());
System.out.println(Arrays.toString(a.getConstructors()));
System.out.println(Arrays.toString(a.getDeclaredFields()));
System.out.println(Arrays.toString(a.getMethods()));
}
}
通过反射调用类中的方法
1)通过Class类的getMethod(String name,Class.parameterTypes)取得一个Method的对象
2)使用invoke(obj,args)方法进行调用
public class invokedemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
Class<?> a=Class.forName("reflect.Person");
Method method=a.getMethod("setAge", int.class);
Person p=new Person();
method.invoke(p,18);
System.out.println(p);
}
}