浅析java反射(字节码文件)

什么是反射?


先谈谈java程序的运行步骤吧! 先编译后运行对吗?
其实你想一想, 你写的java代码机器真的能认识吗? 早在以前就听过了吧机器是只认识0和1的
所以编译这一阶段也就是将java文件编译成字节码文件也就是.class文件 也就是01码

那什么又是反射呢?
我现在觉得反射就是拿到它的字节码文件,对字节码文件做操作


字节码文件


我先举个例子什么叫做字节码文件
这里写图片描述
怎么样 刺不刺激 你也可以对着写下来放在text文件中 后缀改成.class 文件 然后看看他运行的结果是什么
应该是hello world
一大串我们看不懂的01编码 但是java的开发者们应该是通过某一种编码方式开发了java语言
其实你可以想一想 真正机器能识别的就是这个字节码文件 如果你知道了这种编码方式 你是不是其实也可以改这个运行结果呢?
不通过java代码改 直接改class文件 比如把输出的hello wolrd 改成 hello java 可能比较复杂
因为我自学的时间也只有2个月左右 所以帮不到大家 还是回归正题


字节码文件的大致介绍


这一套编码方式 你可以去找找看 应该可以找到
这里面都会加载一些什么 比如 这么多字节码文件会加载你的class_info 也就是类的信息
你看一下第一个cafe babe 你可以查一下 他代表着你这个类是 class类型
然后后面的01码基本上就是 常量池的索引 或者字段信息 方法信息 父类的信息 初始化也就是构造器的信息
当然 我们自然是看不懂 机器能看懂 可能开发者们也能看懂


反射


回到了正题
我们java 的类加载器 在第一次访问一个类的时候 肯定要把他的类信息加载到jvm里面
只要一加载进来我是不是就可以拿到字节码文件了 也就是那个01码文件
我只要拿到了01码文件 如果我会编码的方式 我是不是可以直接改编码
所以反射就是干这样的一个事情 我直接拿你01码 直接改的01码


得到Class文件的三种方式


把这字节码文件封装成一个类 我们通过三种方式可以拿到它

1.类名.class           
说明: JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作.返回Class的对象



2.Class.forName("类名字符串")  
(注:类名字符串是包名+类名)  说明:装入类,并做类的静态初始化,返回Class的对象



3.实例对象.getClass()  
说明:对类进行静态初始化、非静态初始化;
返回引用o运行时真正所指的对象(因为:子对象的引用可能会赋给父对象的引用变量中)
所属的类的Class的对象

上述的方法还是推荐使用Class.forName的方式


反射的Method


不管是 getDeclaredMethod();
还是   getMethod();

这两个方法要的参数都是名字 加上参数的类型 注意 这里的 参数类型也得是字节码类型 也就是.class形式
因为有重载 所以你只有方法的名字是不够的 所以还需要你的参数类型
在你这个类的字节码文件中 总是有一处标识着Method字段 然后可能就是他的权限修饰符 返回值 方法名 及其参数等等
所以你传入名字 会通过字节码文件去找这个方法 然后 根据你传过来的类型的字节码匹配所对应的重载方法

如果你使用的是getDeclaredMethod() 这个方法
其实我们也都知道这个方法 连私有的方法都可以拿到
因为我拿你字节码 我又不管你是谁 但是你是私有的 其实还是不想让外界访问到

所以出现了setAccessible(true);这样的一个方法出现


反射的Field


其实类的Field 也是一样的 总有字节码去标记字段的开始 权限 类型 昵称等等 这些都写在了字节码文件中
其实到这也能很好的解释了 你java代码变了 为什么要重新编译 因为其实起决定性作用的并不是你的java代码
而是编译出来的字节码文件
所以这里不再多说废话 一会总结一下 反射的方法 并且写个反射的妙一点的例子


反射的构造器Constructor


因为前面的字节码文件已经说了很多 这里就是通过 Class 拿到类构造器
然后可以去new对象


反射方法总结


1.类名.class           
说明: JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作.返回Class的对象



2.Class.forName("类名字符串")  
(注:类名字符串是包名+类名)  说明:装入类,并做类的静态初始化,返回Class的对象



3.实例对象.getClass()  
说明:对类进行静态初始化、非静态初始化;
返回引用o运行时真正所指的对象(因为:子对象的引用可能会赋给父对象的引用变量中)
所属的类的Class的对象

这里我们假设有个Student类 至于细节什么的 应该都懂

        //一、通过getclass 返回Class对象
        s.getClass(); //返回Class对象
        c.getSuperclass(); //返回Class对象
        //二、类名.calss
        Student.class; //返回Class对象
        //三、通过forname
        Class.forName("com.sxt.User"); //返回Class对象
        //四、包装类的反射
        Integer.TYPE; //返回Class对象
        //获取全路径名字
        System.out.println(c2.getName());
        //获取类名
        System.out.println(c2.getSimpleName());
        //获取权限,1、public 2、protected 3、default 4、private
        System.out.println(c2.getModifiers());
        //判断是否是接口
        System.out.println(c2.isInterface());
        //返回public属性
        Field[] fields = c2.getFields();
        //返回全部属性
        Field[] declaredFields = c2.getDeclaredFields();
        //返回一个public属性对象
        Field field = c2.getField("name");
        //返回一个属性对象
        Field declaredField = c2.getDeclaredField("name");
        //public方法数组
        Method[] methods = c2.getMethods();
        //所有方法数组
        Method[] declaredMethods = c2.getDeclaredMethods();
        //返回一个权限是public方法名是run,参数类型为空的方法对象
        Method m =c2.getMethod("run",null);
        //返回一个方法名是run,参数类型是String和int 的method 类型
        Method m1=c2.getDeclaredMethod("run", String.class,int.class);
        //返回public构造函数数组
        Constructor[] constructors = c2.getConstructors();
        //返回所有构造函数数组
        c2.getDeclaredConstructors();
        //返回一个构造函数是参数是public的String,int类型的数组
        c2.getDeclaredConstructor(String.class,int.class);
        //返回一个构造函数列表为空的数组
        c2.getConstructor(null);
        //创建对象
        Student stu1 = (Student)c2.newInstance();
        //构造函数创建对象
        Student  stu2 = (Student)ct1.newInstance();
        //构造函数创建有参对象
        (Student)ct2.newInstance(String.class,Integer.class); // 

反射的一个小例子


这里我需要提一下工厂模式 因为我用到了工厂模式
但是也不一定工厂模式就要用反射 我没这样说哈 我就是在这个具体例子中可能会用到


工厂模式

工厂模式(Factory Pattern) 是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式.
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

就比如 你需要一辆汽车, 你可以直接从工厂里面提货. 你不用去管这辆汽车是怎么做出来的.和这个汽车的具体实现 你只需要名字就可以

就比如我们现在有这样的一个定义

interface Car {
    public void payFor();//支付价钱的功能
}

然后我们有三种车SuperCar Bus和EasyCar 他们共同实现了这个借口

class SuperCar implements Car {
    @Override
    public void payFor() {
        System.out.println("兰博基尼 : 您需要支付500万元");
    }
}

class Bus implements Car {
    @Override
    public void payFor() {
        System.out.println("公交车 : 您需要支付100万元");
    }
}

class EasyCar implements Car {
    @Override
    public void payFor() {
        System.out.println("小轿车 : 您需要支付20万元");
    }
}

想一想 我们去买车 是不是告诉他名字 那我们可以有个工厂

class CarFactory {
    private CarFactory() {}
    public static Car getCar(String name) {
        if (name.equalsIgnoreCase("SuperCar")) { //如果是SuperCar
            return new SuperCar();
        } else if(name.equalsIgnoreCase("Bus")) { // 如果是Bus
            return new Bus();
        } else if(name.equalsIgnoreCase("EasyCar")) { // 如果是EasyCar
            return new EasyCar();
        } else {
            return null;
        }
    }
}

这样你把名字拿来我就能给你造出来 对不对 但是 这样你不觉得有点麻烦嘛 我后来有n多个汽车 而且事实也是这样的
我们现在兰博基尼 玛莎拉蒂 法拉利等等 对吧 太多了
你这个工厂还要一个一个添加, 这是不是有点麻烦
所以你只要把你的字节码文件给我 我直接用字节码文件给你new就行了

class CarFactory {
    private CarFactory() {}
    public static Car get(Class t) throws Exception {
        if(t == null)
            return null;
        return (Car)t.getDeclaredConstructor().newInstance();
    }
}

这样不管你多少种车 我就这一句话 是不是减少了代码的沉余呢?
好了 完整代码 带测试

package NewDay11;

interface Car {
    public void payFor();
}

class SuperCar implements Car {
    @Override
    public void payFor() {
        System.out.println("兰博基尼 : 您需要支付500万元");
    }
}

class Bus implements Car {
    @Override
    public void payFor() {
        System.out.println("公交车 : 您需要支付100万元");
    }
}

class EasyCar implements Car {
    @Override
    public void payFor() {
        System.out.println("小轿车 : 您需要支付20万元");
    }
}

class CarFactory {
    private CarFactory() {}
    public static Car get(Class t) throws Exception {
        if(t == null)
            return null;
        return (Car)t.getDeclaredConstructor().newInstance();
    }
}


public class FactoryText {
    public static void main(String[] args) throws Exception{
        Car c = CarFactory.get(Class.forName("NewDay11.SuperCar"));
        c.payFor();
    }
}



猜你喜欢

转载自blog.csdn.net/qq_42011541/article/details/81385839