反射(reflect)
-
Java程序的运行过程
源文件(java)–》javac 编译器编译为class字节码文件–》类加载器加载到内存中(方法区中code segment)–》字节码校验器进行校验–》解释器进行解释–》操作系统执行
-
被类加载器加载到内存的字节码文件其实就是一个又一个的Class(Filed,Mehtod,Constructor)的对象,把这些对象放大来看,里面的成员变量和方法都是对象
1.1.什么是反射
- 动态的获取类的信息以及对类进行操作的机制;
- 反射是框架的灵魂;
- 目的:解耦,程序的可扩展性
1.2.反射的使用
1.获取Class类对象
常用的是Class.forName(“类的全路径名”);
public static void main(String[] args) throws ClassNotFoundException {
// 1, 类名.class
Class<Student> studentClass = Student.class;
// 2, 对象.getClass()
Student student = new Student();
Class<? extends Student> aClass = student.getClass();
// 3, Class.forName("类的全路径"); 包名+类名
Class<?> aClass1 = Class.forName("com.qy28.sm.Student");
}
2.获取class类对象的方法
public static void main(String[] args) throws ClassNotFoundException {
// 3, Class.forName("类的全路径"); 包名+类名
Class<?> aClass = Class.forName("com.qy28.sm.Student");
//getMethods() 获取所有public的方法 包括继承下来的
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
//getDeclaredMethods获取本类中声明的方法(所有的),不包含继承的
System.out.println("=============");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
}
3.获取class对象的成员变量
public static void main(String[] args) throws ClassNotFoundException {
// 3, Class.forName("类的全路径"); 包名+类名
Class<?> aClass = Class.forName("com.qy28.sm.Student");
// getFields(); 获取所有public的成员变量 包括继承下来的
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
// getDeclaredFields() 获取本类中声明的成员变量(所有的),不包含继承的
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField.getName());
}
}
4.获取构造器,如何获取指定的构造器
public static void getConstructor() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// Class.forName("类的全路径"); 包名+类名
Class<?> aClass = Class.forName("com.qy28.sm.Student");
//获取构造器
Constructor<?>[] constructors = aClass.getConstructors();
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}
// 获取的是指定的无参的构造器
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
Object o = declaredConstructor.newInstance();
System.out.println(o);
// 获取的是指定的有参的构造器
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class);
Object tom = declaredConstructor1.newInstance("anne");
System.out.println(tom);
}
5.获取指定的方法并进行调用, 重点掌握
public static void getDeclaredMethod() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// Class.forName("类的全路径"); 包名+类名
Class<?> aClass = Class.forName("com.qy28.sm.Student");
// 调用的是无参构造器
Object o = aClass.newInstance();
// 获取指定方法 无参
Method declaredMethod = aClass.getDeclaredMethod("getName");
// 通过反射进行方法调用
declaredMethod.invoke(o);
Method study = aClass.getDeclaredMethod("study", String.class, int.class);
study.invoke(o, "abc", 12);
}
6.反射的使用案例
public static void test() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
// 解耦
// Student student = new Student("", new DellComputer());
Class<?> aClass = Class.forName("com.qy28.sm.Student");
// 获取学生构造器 Spring
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, Computer.class);
// 获取电脑的class类对象
Class<?> aClass1 = Class.forName("com.qy28.sm.HuaComputer");
// 获取电脑的指定构造器
Constructor<?> declaredConstructor1 = aClass1.getDeclaredConstructor(String.class);
// 实例化一个电脑实例
Object computer = declaredConstructor1.newInstance("hua");
// 实例化学生对象
Object tom = declaredConstructor.newInstance("tom", computer);
System.out.println(tom);
}
1.3. 类的加载过程
1.动态的加载;按需加载,用的时候才会加载这个类的字节码文件
2.类加载器有哪些?
-
核心加载器(bootstrap class loader)
加载最核心的类;是本地语言写的,没有名字
-
扩展类加载器
加载的是jre/lib/ext包下面的类
-
应用类加载器
加载的是用户自定义的类
-
其他加载器
3.双亲委派机制–防止字节码文件被重复加载
当类加载器收到一个加载类的请求的时候,会把请求先委派为父类,父类也会继续把请求往上委派,最终请求都会到达核心类加载器,如果在核心类加载器的加载范围就会去加载否则是加载失败继续往回追;如果父级都加载失败了才会由当前的类加载器进行加载
public static void main(String[] args) throws ClassNotFoundException {
/* Class<?> aClass = Class.forName("com.qy28.sm.load.A");//ClassNotFoundException
System.out.println("=================");*/
/* System.out.println(String.class.getClassLoader());
System.out.println(AccessBridge.class.getClassLoader());
System.out.println(LoadTest.class.getClassLoader());*/
Class<LoadTest> loadTestClass1 = LoadTest.class; // AppClassLoder
Class<LoadTest> loadTestClass2 = LoadTest.class; // ExtClassLoader
Class<?> aClass = Class.forName("com.qy28.sm.load.LoadTest");
LoadTest loadTest = new LoadTest();
// AccessBridge.class.getClassLoader().loadClass("com.qy28.sm.load.LoadTest");
System.out.println(loadTestClass1 == aClass);
}