字节码对象, 当我们保存后程序产生的. class 文件是编译后的文件, 当我们运行程序的时候, 程序去读取. class 文件, 这个文件会存到内存中, 在堆中创建一个. class 文件对象
当程序要使用某个类的时候, 该类还没有到内存中去, 会通过加载, 连接, 初始化 3 步对这个类进行初始化
加载, 将 class 文件对象加载到内存中去, 创建一个 class 对象, 任何类在被使用时都会创一个 class 对象
连接, 1. 检验, 检查语法是否有问题, 跟其他类是否协调
2. 准备, 给静态成员变量初始化值
3. 解析, 将符号引用改成直接引用
例如: int a = 1; 解析就是把 a 直接改成 1 了
类什么时候初始化
1. 创建类的时候
2. 类的静态方法
3. 类的静态成员变量赋值的时候
4. 反射创建某个类的时候
5. 调用某个类的子类的时候
6. 直接运行程序的时候
类的加载器组成
反射
对于 java 中任意一个类, 我们可以知道他的方法和属性, 对于任意一个对象, 我们可以调用他的方法和属性, 解剖出来, 这叫做 java 的反射机制
首先我们要获取这个类的字节码对象, 对他进行操作
有 3 种方式
我们先创建一个类 person
- package com.orcale.damo01;
- public class Person {
- public String name;
- private int age;
- static{
- System.out.println("静态代码块");
- }
- public Person(){
- System.out.println("空参构造");
- }
- public Person(String name,int age){
- this.name= name;
- this.age= age;
- System.out.println("有参构造");
- }
- private Person(int age,String name){
- this.age = age;
- this.name= name;
- System.out.println("有参构造");
- }
- public void eat(){
- System.out.println("吃饭");
- }
- public void work(String name){
- System.out.println(name+"走路");
- }
- private void run(String name){
- System.out.println(name+"跑步");
- }
- @Override
- public String toString() {
- return "Person [name=" + name + ", age=" + age + "]";
- }
- }
通过对象获取字节码对象
- 1 Person p = new Person();
- 2 Class c = p.getClass();
- System.out.println(c);
通过类名获取
1 Class c1 = Person.class;
通过 class 的静态方法获取 forName(完成的类名, 包名)
- Class c2 = Class.forName("com.orcale.damo01.Person");
- System.out.println(c2);
获取公共的构造方法
- Class c = Person.class;
- Constructor con =c.getConstructor(这里如果是有参构造就写参数);
就表示为
Constructor con =c.getConstructor(Sting.calss,int.class) 数据类型在前, class 在后面, 具体看构造参数
3 System.out.println(con);
获取所有的公共的构造方法
Class c = Person.class;
Constructor[] con = c.getConstructors(); 一个对象数组, 把所有的构造方法都放进去
- for(Constructor co:con){
- System.out.println(co);
- }
获取公共的方法
- Class c = Person.class;
- Constructor con =c.getConstructor(String.class,int.class);
- // 因为 Person 类中没有单独 String name 的构造方法, 所以写了 2 个,
- Object obj = c.newInstance();
- // 创建一个对象祖宗类, 然后用字节码对象获取方法
- //getMethod("第一个写需要调用的方法名字", 写需要传入的参数);
- Method me = c.getMethod("work", String.class);
- // 调用方法用 invoke 方法 (第一个写对象, 第二个写需要传入的实参)
- me.invoke(obj, "小明");
获取所有的公共方法和获取所有的公共构造方法差不多, 整个数组遍历就可以了
获取所有的公共成员变量
- Class c= Person.class;
- Field[] f= c.getFields(); // 通过 Field[] 方法数组
- for(Field ff:f){
- System.out.println(ff);
- }
获取指定的公共成员变量
- Class c= Person.class;
- Field f= c.getField("name");
- System.out.println(f);
修改公共的成员变量赋值
- Class c= Person.class;
- Field f= c.getField("name");
- Object obj = c.newInstance();
- // 调用 set 方法对 name 赋值, 然后输出对象的属性
- f.set(obj, "小明");
- System.out.println(obj)
获取私有的构造方法
原理是通过 setAccessible 方法, 避开了对类的加载器对类的检查, 不建议对私有的构造方法和属性操作, 不然还私有干啥没有意义了
- Class c =Person.class;
- Constructor con = c.getDeclaredConstructor(int.class,String.class);
- con.setAccessible(true);// 暴力反射开关
- Object obj = con.newInstance(12,"dd");
- System.out.println(obj);
获取私有的成员变量
- Class c= Person.class;
- Field field = c.getDeclaredField("age"); // 获取私有的的成员变量
- Object obj = c.newInstance();
- field.setAccessible(true); // 暴力开关
- field.set(obj, 11);
- System.out.println(obj);
练习: 有一个 AarrayList<String> list, 然后往里面添加 int 类型数据, 泛型擦除 (字节码文件中没有泛型)
- ArrayList<String> arr = new ArrayList<String>();
- arr.add("abc");
- Class c= arr.getClass();
- Method addd = c.getMethod("add", Object.class);
- addd.invoke(arr, 1);
- for(Object obj:arr){
- System.out.println(obj);
- }
来源: https://www.cnblogs.com/wangrongchen/p/9242789.html