1. 类加载器
1.1 类加载
类加载或类初始化的三个步骤:类的加载、类的连接、类的初始化
加载:类加载过程的一个阶段:通过一个类的完全限定查找此类字节码文件,并利用字节码文件创建一个Class对象
验证:目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即0(如static int i=5;这里只将i初始化为0,至于5的值将在初始化时赋值),这里不包含用final修饰的static,因为final在编译的时候就会分配了,注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。
解析:主要将常量池中的符号引用替换为直接引用的过程。符号引用就是一组符号来描述目标,可以是任何字面量,而直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。有类或接口的解析,字段解析,类方法解析,接口方法解析(这里涉及到字节码变量的引用,如需更详细了解,可参考《深入Java虚拟机》)。
初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。
当一个类被载入了JVM,就不会被再次载入了,只载入一次。
1.2 类加载器
- ClassLoader 类名.class.getClassLoader():获取.class文件的类加载器
- InputStream getResourceAsStream(String name):从classpath根目录下查找name资源
代码示例:
public class ClassLoaderDemo { public static void main(String[] args) { // static ClassLoader getSystemClassLoader (): 返回用于委派的系统类加载器 ClassLoader c = ClassLoader.getSystemClassLoader(); System.out.println(c); // AppClassLoader // ClassLoader getParent (): 返回父类加载器进行委派 ClassLoader c2 = c.getParent(); System.out.println(c2); // ExtClassLoader ClassLoader c3 = c2.getParent(); System.out.println(c3); // null
ClassLoader c4 = Student.classs.getClassLoader(); //AppClassLoader
InputStream is = c4.getResourceAsStream("pro.properties");//从根路径src下查找pro.properties文件 } }
运行结果:
AppClassLoader:系统类加载器(System)
ExtClassLoader:平台类加载器(Platform)
null:内置类加载器(Bootstrap),所以它控制台输出为null
2. 反射
2.1 反射概述
深入理解反射机制
Student和Teacher,要想使用就要通过类加载器加载对象的.class文件到内存中去,而每一个.class文件中都应该包含,成员变量、构造方法、成员方法等,每个class文件都包含这些信息,而Class就是所有.class文件对应的类型,Class就可以使用,成员变量、构造方法、成员方法等,就不需要Student和Teacher了,这就叫反射。
2.2 获取Class类的对象
第一个方法比较简洁,
第三个方法比较的灵活,
一般使用推荐使用第三个方法。
代码示例:
获取Class类的对象就要有一个对象,所以要创建一个学生类
学生类:
public class Student { // 成员变量,一个私有,一个默认,一个公共 private String name; int age; public String address; // 构造方法,一个私有,一个默认,两个公共 public Student() { super(); } private Student(String name) { super(); this.name = name; } Student(String name, int age) { super(); this.name = name; this.age = age; } public Student(String name, int age, String address) { super(); this.name = name; this.age = age; this.address = address; } // 成员方法,一个私有,四个公共 private void function() { System.out.println("function"); } public void method1() { System.out.println("method"); } public void method2(String s) { System.out.println("method:" + s); } public String method3(String s, int i) { return s + "," + i; } @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", address=" + address + "]"; } }
测试类:
//三种方式获取Class对象 public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { // 1:使用类的class属性来获取该类对应的Class对象。举例: Student. class将会返回Student类对应的Class对象 Class<Student> c1 = Student.class; System.out.println(c1); Class<Student> c2 = Student.class; System.out.println(c1 == c2); // 一个类在内存中只有一个字节码文件对象,所以输出为true System.out.println("---------------------"); // 2:调用对象的getClass()方法,返回该对象所属类对应的Class对象 // 该方法是Object类中的方法,所有的Java对象都可以调用该方法 Student s = new Student(); Class<? extends Student> c3 = s.getClass(); System.out.println(c1 == c3);// 如果是true就代表通过该方法也可以得到该类的字节码文件 System.out.println("---------------------"); // 3:使用Class类中的静态方法forName(String className), 该方法需要传入字符串参数,该字符串参数的值是某个类的全路径 Class<?> c4 = Class.forName("myRrflect01.Student"); System.out.println(c1 == c4);// 如果是true就代表通过该方法也可以得到该类的字节码文件 } }
运行结果:
2.3 反射获取构造方法并使用
代码示例:
对象使用的是2.2中的Student学生类
首先通过forName()得到字节码文件Class对象,通过Class对象的getConstructor()得到单个的构造方法,然后通过构造方法的newInstance()方法得到它的构造方法来创建对象。这就是反射。
public class ReflectDemo01 { public static void main(String[] args) throws Exception { // 获取Class对象 Class<?> c = Class.forName("myRrflect01.Student"); // Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组 Constructor<?>[] cons1 = c.getConstructors(); for (Constructor<?> con1 : cons1) { System.out.println(con1); } System.out.println("------------------"); // Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组 Constructor<?>[] cons2 = c.getDeclaredConstructors(); for (Constructor<?> con2 : cons2) { System.out.println(con2); } System.out.println("------------------"); // Constructor<T> getConstructor(Class<?> .. parameterTypes):返回单个公共构造方法对象 // Constructor<T> getDeclaredConstructor(Class<?... parameterTypes):返回单个构造方法对象 // 参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象 // 取一个无参构造方法 Constructor<?> con = c.getConstructor();// 因为是无参,所以不需要添加参数。 // Constructor提供了一个类的单个构造函数的信息和访问权限 // Constructor类中用于创建对象的方法 // T newlnstance(Object... initargs):根据指定的构造方法创建对象 Object obj = con.newInstance(); System.out.println(obj ); // 这是不适用反射的方法 // Student s = new Student(); // System.out.println(s); } }
运行结果:
2.4 反射获取构造方法并使用练习
2.4.1 练习1
代码示例:
对象使用的是2.2中的Student学生类
//通过反射实现如下的操作: //Student s = new Student("林青霞”, 30, "西安"); //System. out. println(s); public class RefectDemo02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 获取Class对象 Class<?> c = Class.forName("myRrflect01.Student"); // 获取构造方法对象 // public Student(String name, int age, String address) // Constructor<T> getConstructor (Class<?>... parameterTypes ),返回单个公共构造方法对象 // 基本数据类型也可以通过. class得到对应的Class类型 Constructor<?> con = c.getConstructor(String.class, int.class, String.class); // 利用构造方法得到对象 // T newInstance (object... initargs),根据指定的构造方法创建对象 Object obj = con.newInstance("林青霞", 30, "西安"); System.out.println(obj); } }
2.4.2 练习2
代码示例:
对象使用的是2.2中的Student学生类
//通过反射实现如下的操作: //Student s = new Student("林青霞"); //System. out. println(s); public class RefectDemo03 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 获取Class对象 Class<?> c = Class.forName("myRrflect01.Student"); // 获取构造方法对象 // private Student(String name) // Constructor<T> getDeclaredConstructor (Class<?>... parameterTypes),返回单个构造方法对象 Constructor<?> con = c.getDeclaredConstructor(String.class); // 因为私有的构造方法是不能创建对象的,但是在反射里面是可以的,这就需要使用暴力反射 // public void setAccessible (boolean flag) :值为true,取消访问检查 con.setAccessible(true); // 利用构造方法得到对象 // T newInstance (object... initargs),根据指定的构造方法创建对象 Object boj = con.newInstance("林青霞"); System.out.println(boj); } }
运行结果:
2.5 反射获取成员变量并使用
代码示例:
首先获取Class对象,通过Class对象的getFields ()方法获取指定的成员变量,得到成员变量对象,通过成员变量对象调用set()方法,给对象的成员变量赋值。
对象使用的是2.2中的Student学生类
//反射获取成员变量并使用 public class ReflectDemo02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 获取Class对象 Class<?> c = Class.forName("myRrflect01.Student"); // Field[] getFields ()返回一个包含Field对象的数组,Field对象反 映由该Class对象表示的类或接口的所有可访问的公共字段 Field[] fields = c.getFields(); for (Field field : fields) { System.out.println(field); } System.out.println("----------------"); // Field[] getDeclaredFields () 返回一个Field对象的数组,反映了由该Class对象 表示的类或接口声明的所有字段 Field[] fields2 = c.getDeclaredFields(); for (Field field2 : fields2) { System.out.println(field2); } System.out.println("----------------"); // Field getField (String name) 返回一个Field对象, 该对象反映由该Class对象表示的类或接口的指定公共成员字段 // Field getDeclaredField (String name)返回个Field对象, 该对象反映由该Class对象 表示的类或接口的指定声明字段 Field addressField = c.getField("address"); // 获取无参构造方法获取对象 Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); // Field提供有关类或接口的单个字段的信息和动态访问 // void set (object obj, object value) 将指定的对象参数中由此Field对象表示的字段设置为指定的新值 addressField.set(obj, "西安");// 给obj的成员变量addressField赋值为西安 System.out.println(obj); } }
运行结果:
2.5.1 反射获取成员变量并使用练习
代码示例:
对象使用的是2.2中的Student学生类
public class ReflectDemo02 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { // 获取Class对象 Class<?> c = Class.forName("myRrflect01.Student"); // 获取无参构造方法创建对象 // Student s = new Student( ); Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); System.out.println(obj); // s. name ="林青霞”; // 因为name变量是私有的,所以使用getDeclaredField()方法获取,同时也要使用暴力反射setAccessible()方法为true Field nameField = c.getDeclaredField("name"); nameField.setAccessible(true); nameField.set(obj, "林青霞"); System.out.println(obj); // 为了方便也可以每次使用getDeclaredField()获取变量和setAccessible(true);取消访问检查 // s.age = 30; Field ageField = c.getDeclaredField("age"); ageField.setAccessible(true); ageField.set(obj, 30); System.out.println(obj); // s. address = “西安”; Field addressField = c.getDeclaredField("address"); addressField.setAccessible(true); addressField.set(obj, "西安"); System.out.println(obj); } }
运行结果:
2.6 反射获取成员方法并使用
首先获取Class对象,然后通过getMethod()方法得到成员方法对象,最后通过成员方法对象调用invoke()方法传入对象,就是调用该对象的方法。
代码示例:
对象使用的是2.2中的Student学生类
//反射获取成员方法并使用 public class ReflectDemo03 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 获取Class对象 Class<?> c = Class.forName("myRrflect02.Student"); // Method[] getMethods() 返回一个包含方法对象的数组 // 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。 Method[] methods1 = c.getMethods(); for (Method method1 : methods1) { System.out.println(method1); } System.out.println("---------------------"); // Method[] getDeclaredMethods() 返回一个包含方法对象的数组 // 方法对象反射的类或接口的所有声明的方法,通过此表示类对象,包括公共,保护,默认(包访问和私有方法,但不包括继承的方法。 Method[] methods2 = c.getDeclaredMethods(); for (Method method2 : methods2) { System.out.println(method2); } System.out.println("---------------------"); // Method getMethod(String name, 类<?>... parameterTypes) // 返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。 // Method getDeclaredMethod(String name, 类<?>... parameterTypes) // 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。 // 获取public void method1() Method m = c.getMethod("method1"); // 获取无参构造方法创建对象 Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); // 在类或接口.上提供有关单一方法的信息和访问权限 // Object invoke (Object obj, Object... args) 在具有指定参数的指定对象.上调用此方法对象表示的基础方法 // Object:返回值类型 // obj:调用方法的对象 // args:方法需要的参数 m.invoke(obj); } }
运行结果:
最后输出了Student类中method1()成员方法
2.6.1 反射获取成员方法并使用练习
代码示例:
对象使用的是2.2中的Student学生类
public class ReflectDemo04 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 获取Class对象 Class<?> c = Class.forName("myRrflect02.Student"); // Student s = new Student(); Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); // s.method1(); Method m1 = c.getMethod("method1"); m1.invoke(obj); // s.method2( "林青霞"); Method m2 = c.getMethod("method2", String.class); m2.invoke(obj, "林青霞"); // String ss = s. method3("林青霞", 30); // System.out.println(ss); Method m3 = c.getMethod("method3", String.class, int.class); Object method3 = m3.invoke(obj, "林青霞", 30); System.out.println(method3); // s.function(); // 这是一个私有方法,所以使用getDeclaredMethod()获取和setAccessible(true);取消访问检查 Method m4 = c.getDeclaredMethod("function"); m4.setAccessible(true); m4.invoke(obj); } }
运行结果:
2.9 反射练习
2.9.1 练习1
向integer集合中添加字符串类型的数据,原本是不可能实现的,但是在反射中是可以的。
代码示例:
public class ReflectTest01 { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 创建集合 ArrayList<Integer> array = new ArrayList<Integer>(); // 获取集合的Class对象 Class<? extends ArrayList> c = array.getClass(); // 调用集合Class对象的add方法 Method m = c.getMethod("add", Object.class); // 添加数据 m.invoke(array, "hello"); m.invoke(array, "world"); m.invoke(array, "java"); System.out.println(array); } }
运行结果:
2.9.2 练习2
这样使用灵活性较强
代码示例:
学生(Student)类:
public class Student { public void study() { System.out.println("好好学习,天天向上"); } }
教师(Teacher)类:
public class Teacher { public void teach() { System.out.println("用爱成就学生"); } }
配置文本:
class.text
测试类:
public class ReflectTest02 { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 加载数据 Properties prop = new Properties(); FileReader fr = new FileReader("src\myRrflect03\class.text"); prop.load(fr); fr.close(); // className=myRrflect03.Student // methodNmae=study String className = prop.getProperty("className"); String methodName = prop.getProperty("methodNmae"); // 通过反射来使用 Class<?> c = Class.forName(className);// myRrflect03.Student Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); Method m = c.getMethod(methodName);// study m.invoke(obj); } }
运行结果:
修改配置类运行: