笔记——类型信息与反射
2.1.1 .class文件和.java文件
Java的编译器在编译Java类文件时,会将原有的文本文件翻译成二进制的字节码,并将这些字节码存储在.class文件中。对于只含有一个类或接口的Java类文件,编译后只产生一个.class文件;而对于一个含有多个类或接口的Java类文件,分情况为:①.Java只含一个类或接口,编译后只产生一个.class文件;②.Java存在内部类时,会产生多个.class文件,内部类文件命名为:外部类名+$+内部类名,③.Java文件中存在多个并行类或接口,编译时也会产生多个.class文件,文件命名为类名/接口名。
2.1.2 类型信息的加载
Java提供两种加载方式:①预先装载②按需装载
按需装载类的条件: 在程序运行过程中,当一个类的静态成员被第一次引用时,JVM就会去装载它,静态成员包括:静态方法、静态属性、构造方法。
类加载器:三种
1)bootstrap类加载器用于引导JVM,一旦调用Java.exe程序,bootstrap类加载器就开始工作。
2)extension类加载器负责加载标准扩展目录下的类,是编写程序变得简单。
3)system加载器是默认的加载器,它在环境变量CLASSPATH目录下查找相应的类。
表2-2 ClassLoader中与加载类相关的方法
方法 | 说明 |
---|---|
getParent() | 返回该类加载器的父类加载器 |
loadClass(String name) | 加载名称为name的方法,返回java.lang.Class类的实例 |
findClass(String name) | 查找名称为name的类,返回java.lang.Class类的实例 |
findLoadedClass(String name) | 查找名称为name的已经被加载过的类,返回java.lang.Class类的实例 |
resolveClass(Class<?> c) | 链接指定的Java类 |
类加载的顺序:当一个类具有继承关系时,装载是从顶级类开始的,以此类推制止加载到这个类本身。
2.2核心类
2.2.1 Class类
获取Class对象的方法有多种:
① xxx.class Class<Person> clazz = Person.class;
②xxx.getClass()方法 Person.getClass()
③xxx.forname()方法 Class<?> clazz = Class.forName(String);
2.2.2获取Constructor对象
Constructor类专门用来描述构造函数
方法 | 说明 |
---|---|
getConstructior(Class parameterTypes... ) | 用于获取para类的的Constructor对象,获取的构造方法必须为公有 |
getConstructors() | 获取制动类的共有构造函数列表,如果没有则返回长度为0的Constructor数组 |
getDeclaredConstructor(Class ... parameterTypes) | 获取(可公有、保护、私有)指定参数类型的构造函数描述对象 |
getDeclaredConstructors() | 获取(所有)构造函数描述的对象列表 |
2.2.3获取Method对象
Method类对象用于描述类的单个方法(不含构造方法)。可通过Method类来获取方法的访问权限、参数类型、返回值类型等信息。
Class类提供了4个方法供编程人员获取方法的描述对象
方法 | 说明 |
---|---|
getMethod(String name,Class...paramrterTypes) | 用于获取指定名称和参数类型的公有方法描述对象(包含继承自父类) |
getMethods() | 用于获取公有的方法描述对象列表(包含继承自父类) |
getDeclaredMethod(String name,Class...paraTypes) | 可获取非公有的指定名称和参数类型的方法描述对象 |
getDeclaredMethods() | 用于获取类本身定义的所有方法描述对象 |
2.2.4获取Field对象
Field类的对象用于描述类的单个字段。可通过对Field对象来获取字段的访问权、字段信息等类型,并且可通过Field对象来动态地修改字段值
方法 | 说明 |
---|---|
getField(String name) | 用于获取指定名称的公有Field对象 |
getFields() | 用于获取指定类的共有属性描述对象Field列表 |
getDeclaredField(String name) | 用于获取(可非公有)的指定名称的Field属性 |
getDeclaredFields() | 获取一份Field数组,该数组记录指定类的所有属性的描述对象 |
2.4类型信息应用——反射
Java反射机制是指运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法,修改它的任意属性;这种动态获取的信息以及动态调用对象成员的功能称为Java语言的反射机制。
反射的应用:
举例:以下是一个继承Person类的Teacher类,教师有职称、薪水属性,能speak和getSalary
package org.ddd.reflect.example29; public abstract class Person{ public abstract String toString(); } package org.ddd.reflect.example29; public final class Teacher extends Person{ public String position; private int salary; public void speak(String message) { System.out.println("Speak:" + message); } @Override public String toString() { return "[Position: "+ position + " Salary: " + salary + "]"; } private int getSalary() { return this.salary; } }
使用反射技术实现以下要求:
①显示的将这个类载入内存
②实例化这个类
③执行方法speak(String message)
④修改属性position
⑤修改受保护属性salary,以及执行私有方法getSalary()
1) 使用Class类提供的显示加载方法forName(String name)
public class Bootsrap { public static String className = "org.ddd.reflect.example29.Teacher"; public static void main(String[] args) { try { System.out.println("开始加载类!"); Class clazz = Class.forName(className); System.out.println("类加载完成!"); }catch(ClassNotFoundException e) { e.printStackTrace(); } } }
运行结果:
2)通过反射实例化类,有两种动态创建对象的方法,如Class类的newInstance方法实例化对象,或Constructor的newInstance方法
//实例1 package org.ddd.reflect.example29; public class Bootsrap { public static String className = "org.ddd.reflect.example29.Teacher"; public static void main(String[] args) { try { Class clazz = Class.forName(className); Person person = (Person)clazz.newInstance(); System.out.println(person.toString()); }catch(Exception e) { e.printStackTrace(); } } } //示例二 import java.lang.reflect.Constructor; public class Bootsrap { public static String className = "org.ddd.reflect.example29.Teacher"; public static void main(String[] args) { try { Class clazz = Class.forName(className); Constructor constructor = clazz.getConstructor(); Person person = (Person)constructor.newInstance(); System.out.println(person.toString()); }catch(Exception e) { e.printStackTrace(); } } }
3)通过反射执行方法
该方法名为“speak”,参数类型为String。class。要动态获取speak方法,首先要获取Method对象,method类拥有invoke方法,如下:
import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class Bootsrap { public static String className = "org.ddd.reflect.example29.Teacher"; public static void main(String[] args) { try { Class clazz = Class.forName(className); Constructor constructor = clazz.getConstructor(); Object teacher = constructor.newInstance(); Method method = clazz.getMethod("speak", String.class); method.invoke(teacher, "Lesson one!"); }catch(Exception e) { e.printStackTrace(); } } }
运行结果:
4)通过反射修改属性,通过Field类的方法set,动态的给属性赋值
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Bootsrap { public static String className = "org.ddd.reflect.example29.Teacher"; public static void main(String[] args) { try { Class clazz = Class.forName(className); Constructor constructor = clazz.getConstructor(); Object teacher = constructor.newInstance(); Field field = clazz.getField("position"); System.out.println(teacher.toString()); field.set(teacher, "Master"); System.out.println(teacher.toString()); }catch(Exception e) { e.printStackTrace(); } } }
运行结果:
5)修改访问权限
正常情况下我们无法访问private修饰的salary,但是constructor的父类AccessbleObject提供了setAccessable(boolean flag)
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Bootsrap { public static String className = "org.ddd.reflect.example29.Teacher"; public static void main(String[] args) { try { Class clazz = Class.forName(className); Constructor constructor = clazz.getConstructor(); Object teacher = constructor.newInstance(); Method method = clazz.getDeclaredMethod("getSalary"); method.setAccessible(true); Integer salary = (Integer)method.invoke(teacher); System.out.println("Teacher salary: " + salary); }catch(Exception e) { e.printStackTrace(); } } }
运行结果:
动态代理模式可参考以下博客:https://www.cnblogs.com/gonjan-blog/p/6685611.html