1. 类加载机制
1.1 当调用java命令来运行某个java程序时,该命令将启动一个jvm进程,同一个jvm中的所有线程,变量都位于同一个进程中,共享该jvm内存空间
1.2 jvm退出条件(满足下列条件之一)
1.2.1 程序正常执行结束
1.2.2 使用System.exit(0)
1.2.3 出现异常时,没有捕获异常
1.2.4 平台强制结束jvm进程
注:jvm进程一旦结束,该进程中的内存中的数据将会丢失
1.3 类加载机制步骤:
1.3.1 类的加载
1.3.1.1 类加载是将类的.class文件(字节码文件)载入到内存中,并为之创建一个java.lang.Class对象,我们称之为字节码对象
1.3.1.2 类的加载过程由类加载器完成,类加载器通常由jvm提供
1.3.2 类的连接
当类被加载进内存之后,系统为之生成一个对应的Class对象,接着把二进制数据合并到jre中
1.3.2.1 验证:检测被加载的类是否有正确的内部结构
1.3.2.2 准备:负责为类的static变量分配内存,并设置默认值
1.3.2.3 解析:把类的二进制数据中的符号引用替换为直接引用
1.3.3 类的初始化
jvm在此阶段对类(主要是static变量)进行初始化
1.3.3.1 如果该类还未被加载和连接,则程序先加载并连接该类
1.3.3.2 如果该类的直接父类还未初始化,则先初始化父类
1.3.3.3 如果类中有初始化语句(静态代码块),则系统依次执行这些初始化语句
2. Class对象
2.1 获取Class对象的三种方式
public class ClassTest { @Test public void test01 () throws Exception { //方式一: 通过对象调用getClass()方法 Student student = new Student(); Class<Student> stuClass1 = (Class<Student>) student.getClass(); //方式二: 通过类名.class属性 Class<Student> stuClass2 = Student.class; //方式三: 通过Class.forName(全限定名) Class<Student> stuClass3 = (Class<Student>) Class.forName("com.tca.thinkInJava.chap14.Student"); //class对象是单例的 System.out.println(stuClass1 == stuClass2); //true System.out.println(stuClass1 == stuClass3); //true System.out.println(stuClass2 == stuClass3); //true } }
2.2 Class对象是单例的(通过上面的Test可知,通过三种方式获取的Class<Student>是同一个)
2.3 九大内置的Class对象
2.3.1 分别是八个基本数据类型的字节码,以及void。即int.class, byte.class等
2.3.2 基本数据类型的字节码与包装类的字节码不是同一份(如int.class与Integer.class是不同的)
public class ClassTest { @Test public void test02() { System.out.println(int.class == Integer.class); //false } }
2.4 数组的Class对象
2.4.1 如何表示数组的Class对象
方式一: 数组对象.getClass()
方式二: 数组类型.class
public class ClassTest { @Test public void test03() { int[] arrI = new int[3]; Class<int[]> class1 = (Class<int[]>) arrI.getClass(); //方式一 Class<int[]> class2 = int[].class; //方式二 System.out.println(class1 == class2); //true } }
2.4.2 所有具有相同维数和相同元素类型的数组共享一份字节码
public class ClassTest { @Test public void test04() { System.out.println(new int[5].getClass() == new int[3].getClass()); //true } }
3. 利用反射获取构造器并调用
class Student { private String name; private int age; private boolean isMale; public Student(){} protected Student(String name){this.name = name;} Student(int age){this.age = age;} private Student(boolean isMale){this.isMale = isMale;} @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", isMale=" + isMale + "]"; } } public class ReflectTest { @Test public void test01() throws Exception { @SuppressWarnings("unchecked") Class<com.tca.thinkInJava.chap14.test.Student> clazz = (Class<Student>) Class.forName("com.tca.thinkInJava.chap14.test.Student"); /* * 1.获取全部公共构造器(public) * public Constructor[] getConstructors() */ Constructor<?>[] constructors = clazz.getConstructors(); System.out.println(constructors.length); // 1 /* * 2.获取全部构造器 * public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有) */ Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); System.out.println(declaredConstructors.length); // 4 /* * 3.获取指定公共构造器(public)并调用 * public Constructor getConstructor(Class... parameterTypes) */ Constructor<Student> constructor1 = clazz.getConstructor(); Student stu1 = constructor1.newInstance(); System.out.println(stu1); /* * 4.获取指定构造器并调用 * public Constructor getDeclaredConstructor(Class... parameterTypes) */ Constructor<Student> constructor2 = clazz.getDeclaredConstructor(boolean.class); constructor2.setAccessible(true);// 必须将访问权限设为true,否则无法调用该构造器 Student stu2 = constructor2.newInstance(true); System.out.println(stu2); } }
4. 利用反射获取方法并调用
class Animal{ public void eat() {System.out.println("eat");} private static void eat(String food) {System.out.println("eat food: " + food);} void eat(String type, String food) {System.out.println("type:" + type + " eat food: " + food);} private void breed(Animal[] animals) { System.out.println(Arrays.toString(animals)); } } public class ReflectTest { @Test public void test02() throws Exception { Class<?> clazz = Class.forName("com.tca.thinkInJava.chap14.test.Animal"); Object animal = clazz.newInstance();//如果类有无参公共构造器,可以直接通过Class对象的newInstance()方法获取 /* * 1.获取全部公共方法(包括继承的) * public Method[] getMethods() */ Method[] methods = clazz.getMethods(); System.out.println(methods.length); /* * 2.获取所有成员方法(包括私有的,但不包括继承的) * public Method[] getDeclaredMethods() */ Method[] declaredMethods = clazz.getDeclaredMethods(); System.out.println(declaredMethods.length); /* * 3.获取指定公共方法 * public Method getMethod(String name,Class<?>... parameterTypes) */ Method method1 = clazz.getMethod("eat"); method1.invoke(animal); Method method2 = clazz.getMethod("toString");//调用父类public方法 System.out.println(method2.invoke(animal)); /* * 4.调用指定方法 * public Method getDeclaredMethod(String name,Class<?>... parameterTypes) */ Method method3 = clazz.getDeclaredMethod("eat"); method3.invoke(animal); Method method4 = clazz.getDeclaredMethod("eat", String.class); method4.setAccessible(true); //设置访问权限,否则无法调用 method4.invoke(animal, "meat"); method4.invoke(null, "grass"); //因为该方法是static静态方法,所以对象参数可以直接传null /* * 5.方法参数为可变参数时,用invoke方法调用时,需要将可变参数(数组)用Object[]再包装一下 */ Method method5 = clazz.getDeclaredMethod("breed", Animal[].class); method5.setAccessible(true); method5.invoke(animal, new Object[]{new Animal[5]}); //方法参数为可变参数时 } }
5. 单例模式用枚举是最安全的
5.1 使用反射可以暴力破解单例模式
class Dog { private Dog(){} private static Dog dog = new Dog(); public static Dog getDog(){return dog;} } public class ReflectTest { @Test public void test03() throws Exception { Class<?> clazz = Class.forName("com.tca.thinkInJava.chap14.test.Dog"); Constructor<?> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); Dog dog1 = (Dog)constructor.newInstance(); Dog dog2 = Dog.getDog(); System.out.println(dog1 == dog2); //false } }
5.2 枚举是最安全的单例
public enum Weekend{ MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } public class ReflectTest { @Test public void test04() throws Exception { Class<?> clazz = Class.forName("com.tca.thinkInJava.chap14.test.Weekend"); Constructor<?> constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); Weekend weekend = (Weekend) constructor.newInstance(); System.out.println(weekend); } }
结果报错,无此构造方法。