反射
类的加载机制
-
jvm 和 类
- 运行带有java主方法的类(main)
- 启动jvm去加载字节码
- 程序何时结束:
- 程序正常结束
- 出现异常,没有捕获异常
- System.exit()方法
- 强制杀进程
- **重点:**jvm进程结束,进程中的所有内存数据丢失。
-
加载机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7CCRHd2-1653839171053)(http://i1.go2yd.com/image.php?url=0MA6WvUbsE)]
-
初始化操作:
- 加载类:class文件(字节码文件)载入内存里面,创建一个字节码对象,类加载使用类加载器(ClassLoader,由jvm提供)
- 连接:字节码加载进内存后,jvm会生成一个class对象,把类中的二进制数据提供给jre
- 验证:验证加载的内是否有正确的结构
- 准备:类里面有常量,需要分配内存地址,并且提供默认值
- 解析:类里面的二进制数据的符号的引用替换成直接引用
- 符号引用:Class文件中(CONSTANCE_CLASS_INFO, )
- 直接引用:直接指向目标指针,相对偏移量
- 初始化:jvm负责对类进行初始化,static
- 如果类没有被加载,则优先加载,并建立连接
- 直接父类没有加载,先加载父类
- 静态代码块优先执行,
反射概述
-
问题:Object obj = new String(“abc”);
- 需求:调用String中的length()方法
- 使用强制类型转换
- 不知道真实类型如何解决?
- 需求:调用String中的length()方法
-
问题:一切皆对象(类属于什么对象?方法属于什么对象?字段属于什么对象?)
- Class 表示所有类
- Method 表示所有的方法
- Filed 表示所有的字段
- Constructor 表示所有的构造器
Class类和Class实例
-
Class表示所有的类
-
Class实例:一份份的字节码(类,接口,枚举,注解)
-
各种类型如何表示?
- String: Class<java.lang.String>
- HashMap: Class<java.util.HashMap>
-
如何获取一个字节码Class对象?
-
//直接使用class类 Class<String> Class1 = String.class; //对象形式 Object obj = new String("abc"); Class<?> Class2 = obj.getClass(); //知道类的全限定类名 java.lang.String Class<?> Class3 = Class.forName("java.lang.String");
-
重点:同一个jvm中,只存在一个类的一份字节码问价你和一份字节码对象
-
使用最多的就是Class.forName(“类的全限定名”);
-
基本数据类型的字节码如何表示?
-
jvm里面已经提前内置好了基本数据类型的实例
-
//基本类型的字节码 Class<Integer> integerClass = int.class; Class<Long> longClass = long.class;
-
包装类型里面提供了一个TYPE常量去获取基本数据类型的类实例
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
-
使用字节码对象证明 int 和Integer不是同一类型
Class<Integer> integerClass2 = int.class; Class<Integer> integerClass3 = Integer.class; System.out.println(integerClass2==integerClass3);//false
-
-
-
类的加载,超重要!!!
1,直接使用类.class
Class<String> Class1 = String.class;
2,使用对象的调用getClass();
Object obj = new String("abc");
Class<?> Class2 = obj.getClass();
3,使用类的全限定名 Class.forName
Class<?> Class3 = Class.forName("java.lang.String");
//自己创建的类
Class<?> aClass = Class.forName
("cn.xiaojun.practice.classdemo.ClassDemo");
反射操作构造器
-
Class–>Constructor 类
-
常用方法(获取私有的加 Declared)
//获取单个构造器 Constructor<?> constructor = aClass.getConstructor(); //获取多个构造器(组) Constructor<?>[] constructors = aClass.getConstructors(); //获取和访问权限无关的构造器 Constructor<?>[] declaredConstructor = aClass.getDeclaredConstructors(); //获取带参的构造器 Constructor<?> constructor1 = aClass.getConstructor(String.class);
-
代码演示
public static void main(String[] args) throws NoSuchMethodException { //获取字节码对象 Class<Student> studentClass = Student.class; //获取公共的无参构造器 System.out.println(studentClass.getConstructor()); //获取私有的带参构造器 Constructor<Student> constructor = studentClass.getDeclaredConstructor(int.class, String.class); //获取全部公共的构造器 Constructor<?>[] constructors = studentClass.getConstructors(); for (Constructor<?> constructor1 : constructors) { System.out.println(constructor1); } //获取所有的 Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } System.out.println(constructor); }
通过反射获取的构造器创建对象
-
为什么要用反射创建对象,而不使用new创建?
- 在框架中,提供给我们字符串,只能使用Class.forName()
-
如何使用反射创建对象?
- 获取类的字节码对象Class.forName()
- 获取构造器对象
- 通过反射使用获取到的构造器创建对象
//获取字节码对象 Class<?> aClass = Class.forName("cn.xiaojun.practice.initobject.Student"); //通过字节码对象获取构造器 Constructor<?> constructor = aClass.getConstructor(); //通过反射构造器创建对象 Object o = constructor.newInstance();
-
私有构造器设置可以访问
declaredConstructor.setAccessible(true);//通过构造器设置可以访问
反射操作方法
反射获取方法对象
获取方法:
//获取类字节码对象
Class<?> aClass = Class.forName("cn.xiaojun.practice.classdemo.method.getmethod.Student");
//获取public的全部方法
Method[] methods = aClass.getMethods();
//获取指定方法的第一个参数是方法名,第二个参数是方法参数类型
Method eat = aClass.getMethod("eat");
Method eat1 = aClass.getMethod("eat", String.class);
使用反射操作方法
-
获取类字节码对象
-
获取方法对象
-
使用反射调用方法
-
invoke(Object obj,Object…args)
- obj表示当前方法的所属对象
- args表示该方法的参数
- Object返回就是方法的返回
-
如果方法私有,需要设置可以接受访问
//获取类的字节码 Class<?> aClass = Class.forName("cn.xiaojun.practice.Student"); //通过字节码对象获取方法对象 带参 Method eat = aClass.getMethod("eat"); //通过反射,调用方法 Object invoke = eat.invoke(aClass.newInstance()); //调用带参方法 Method eat1 = aClass.getMethod("eat", String.class); Object invoke1 = eat1.invoke(aClass.newInstance(), "王曼七");
-
使用反射操作静态方法
-
静态方法不属于实例对象,属于类本身
-
如何执行静态方法?
- 获取类字节码对象
- 获取方法对象
- 执行 invoke(null)
//静态方法的调用 Class<?> aClass = Class.forName("cn.xiaojun.practice.Student"); Method method = aClass.getMethod("print", String.class); Object invoke2 = method.invoke(null,"王曼七");
反射操作字段
-
获取类字节码
-
获取字段
//获取公共的字段 Class<?> aClass = Class.forName("cn.xiaojun.practice.field.Student"); Field[] fields = aClass.getFields(); for (Field field : fields) { System.out.println(field); } //所有的字段 Field[] declaredFields = aClass.getDeclaredFields(); //设置字段值 //类的字节码对象 Class<?> aClass = Class.forName("cn.xiaojun.practice.field.Student"); //由字节码对象创建实例对象 Object o = aClass.newInstance(); //获取类中私有字段age Field age = aClass.getDeclaredField("age"); //设置允许访问 age.setAccessible(true); //设置值 age.set(o, 33); Student o1 = (Student) o; System.out.println(o1.getAge());
类加载器加载 Properties
-
FileInputStream 使用绝对路径
//使用绝对路径加载 Properties properties = new Properties(); FileInputStream inputStream = new FileInputStream ("D:\\idea_workspace\\day17\\src\\cn\\xiaojun\\practice\\properties\\db.properties"); properties.load(inputStream); System.out.println(properties);
-
使用相对路径,字节码的输出目录
Properties properties = new Properties(); ClassLoader Loader = Thread.currentThread().getContextClassLoader(); InputStream resourceAsStream = Loader.getResourceAsStream ("cn/xiaojun/practice/properties/db.properties"); properties.load(resourceAsStream); System.out.println(properties);
-
使用相对路径(相对于当前资源文件的字节码路径)
public class CurrentResourcesDemo {
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
Class<CurrentResourcesDemo> currentResourcesDemoClass = CurrentResourcesDemo.class;
InputStream resourceAsStream = currentResourcesDemoClass.getResourceAsStream("db.properties");
properties.load(resourceAsStream);
System.out.println(properties);
}
}
s = new Properties();
Class currentResourcesDemoClass = CurrentResourcesDemo.class;
InputStream resourceAsStream = currentResourcesDemoClass.getResourceAsStream(“db.properties”);
properties.load(resourceAsStream);
System.out.println(properties);
}
}