[Java-基础]反射_Class对象_动态操作

动态性

动态语言

  • 在程序运行时,可以改变程序结构或变量类型,典型的语言:
    • Python,ruby,javascript
    • 如:
function test(){
    var s = "var a=3;var b=5;alert(a+b);";
    eval(s);
}

Java的动态性

  • C,C++,JAVA不是动态语言,但是JAVA有一定的动态性
  • 我们可以利用反射机制,字节码操作获得类似动态语言的特性
  • JAVA的动态性让编程的时候更加灵活

Class类

反射机制

  • 指的是可以于运行时加载,探知,使用编译期间完全未知的类
  • 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。

Class c=Class.forName("com.User");

  • "com.User"是字符串,是类的路径,可以动态的实时的加载这个类。
  • 加载完类之后,在堆内存中,就产生一个Class类型的对象,一个类只有一个Class对象,这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象像一面镜子,透过这个镜子看到类的结构。所以称之为反射、

先简单写一个com.User

public class User {
    /**
     *
     */
    private int id;
    private int age;
    private String uname;

    public User() {

    }

    public User(int id,int age,String uname){
        super();
        this.id=id;
        this.age=age;
        this.uname=uname;
    }
    public String getUname() {
        return uname;
    }

    public void setUname(String uname) {
        this.uname = uname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

获取Class对象的方式

  • 获取Class对象的三种方式
  • 1 Object ——> getClass();
  • 2 任何数据类型(包括基本数据类型int,数组等)都有一个“静态”的class属性
  • 3 通过Class类的静态方法:forName(String className)(常用)
public class Demo01 {

    public static void main(String []args){
        //第一种  Object ——> getClass();
        User user1 = new User();
        Class clazz1=user1.getClass();
        System.out.println(clazz1.hashCode());

        //第二种 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
        Class clazz2 = User.class;
        System.out.println(clazz2.hashCode());

        //第三种:通过Class类的静态方法:forName(String  className)(常用)
        String path="com.fanshe.User";
        try {
            Class clazz3=Class.forName(path);
            System.out.println(clazz3.hashCode());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

这三种方法得到的hashcode是一样的,这验证了一个类只有一个Class对象:一个工厂可以有很多辆车,但可能只有一张图纸。

反射机制的常见作用

反射的使用主要就是获取到了Class对象之后,通过调用Class对象的方法来操纵。

public class Demo02 {
    public static void main(String[] args) throws NoSuchMethodException {
        //第三种:通过Class类的静态方法:forName(String  className)(常用)
        String path="com.fanshe.User";
        try {
            Class clazz3=Class.forName(path);
            //获得类的名字
            System.out.println("\n获取类的名字: ");
            System.out.println(clazz3.getName());//包名加类名
            System.out.println(clazz3.getSimpleName());//类名

            //获取属性信息
            System.out.println("\n获取属性信息: ");
            Field[] field=clazz3.getFields();
            Field[] field1=clazz3.getDeclaredFields();
            System.out.println(field.length+"  "+field1.length);//前者只能读取public的
            Field field2 = clazz3.getDeclaredField("id");
            for(Field temp:field1){
                System.out.println("属性: "+temp);
            }

            //获取方法的属性
            System.out.println("\n获取方法的属性: ");
            Method[] methods = clazz3.getDeclaredMethods();
            Method method = clazz3.getMethod("setUname", String.class);//单一方法
            Method method1 = clazz3.getMethod("getUname",null);
            for(Method temp:methods){
                System.out.println("方法: "+temp);
            }

            //获得构造器的信息
            System.out.println("\n获得构造器信息: ");
            Constructor[] constructors = clazz3.getDeclaredConstructors();
            for(Constructor temp:constructors){
                System.out.println("构造器: "+temp);
            }
            Constructor constructor = clazz3.getDeclaredConstructor(int.class,int.class,String.class);
            System.out.println("有参构造器:"+clazz3);
         } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

运行结果

获取类的名字: 
com.fanshe.User
User

获取属性信息: 
0  3
属性: private int com.fanshe.User.id
属性: private int com.fanshe.User.age
属性: private java.lang.String com.fanshe.User.uname

获取方法的属性: 
方法: public int com.fanshe.User.getId()
方法: public java.lang.String com.fanshe.User.getUname()
方法: public void com.fanshe.User.setUname(java.lang.String)
方法: public void com.fanshe.User.setId(int)
方法: public int com.fanshe.User.getAge()
方法: public void com.fanshe.User.setAge(int)

获得构造器信息: 
构造器: public com.fanshe.User()
构造器: public com.fanshe.User(int,int,java.lang.String)
有参构造器:class com.fanshe.User

通过反射API的动态操作

public class Demo03 {
    public static void main(String []args){
        //第三种:通过Class类的静态方法:forName(String  className)(常用)
        String path="com.fanshe.User";
        try {
            Class clazz3=Class.forName(path);
        /*通过反射API调用构造方法,构造对象*/

            //其实是调用了User的无参方法
            User user = (User)clazz3.newInstance();
            user.setUname("大王");
            System.out.println(user.getUname());
            //获得方法
            Constructor<User> constructor = clazz3.getDeclaredConstructor(int.class,int.class,String.class);
            //通过实际的参数来调用
            User user1 = constructor.newInstance(1001,18,"美云");
            System.out.println(user1.getUname());
        /*通过反射API调用普通方法*/
           //user1.setUname("小七");
           User user2 =(User) clazz3.newInstance();
            Method method = clazz3.getDeclaredMethod("setUname",String.class);
            method.invoke(user2,"小七");
            System.out.println(user2.getUname());

        /*通过反射API操作属性*/
            User user3 = (User)clazz3.newInstance();
            Field f =clazz3.getDeclaredField("uname");
            f.setAccessible(true);
            f.set(user3,"小赖");
            System.out.println(user3.getUname());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

运行结果明显

大王
美云
小七
小赖

这样写有什么好处呢?可以发现setUname 是字符串,传的参数也是变量,这样的变量我们就可以从文件中读取,可以从数据库中读取,总之可以从其他地方传过来,这样就实现了动态的调用,动态创建类。

参考

[1] https://www.cnblogs.com/nullcodeworld/p/8878747.html

[2] b站:av29578196

全部评论

相关推荐

咦哟,从去年八月份开始长跑,两处实习转正都失败了,风雨飘摇,终于拿到offer了更新一下面试记录:秋招:多部门反复面试然后挂掉然后复活,具体问了啥已经忘了,只是被反复煎炸,直至焦香😋春招:base北京抖音hr打来电话说再次复活,准备面试,gogogo北京抖音一面:六道笔试题:1.promise顺序2.定义域问题3.flat展开4.并发请求5.岛屿数量算法(力扣)深度,广度都写6.忘记了,好像也是算法,难度中等其他问题多是框架底层设计,实习项目重难点~~~秒过😇北京抖音二面:三道笔试题:(为什么只有三道是因为第三道没做出来,卡住了)1.中等难度算法(忘记啥题了,应该是个数组的)2.认识js的继承本质(手写继承模式,深入js的面相对象开发)3.手写vue的响应式(卡在了watch,导致挂掉)---后知后觉是我的注册副作用函数写得有问题,有点紧张了其他题目多是项目拷打,项目亮点,对实习项目的贡献~~~第二天,挂,but立马复活转战深圳客服当天约面深圳客服一面:六道笔试题,由于面过太多次字节,面试官叫我直接写,不用讲,快些写完😋,具体都是些继承,深拷贝(注意对数组对象分开处理,深层次对象,循环引用),加中等难度算法题~~~秒过深圳客服二面:口诉八股大战:大概囊括网络,浏览器渲染原理,动画优化,时间循环,任务队列等等(你能想到的简单八股通通拉出来鞭尸😋)算法题:笔试题6道:1:找出数组内重复的数,arr[0]-arr[n]内的数大小为[1-n],例如[1,2,2,3,3]返回[2,3],要求o(n),且不使用任何额外空间(做到了o(n),空间方面欠佳,给面试官说进入下一题,做不来了)2:原滋原味的继承(所以继承真滴很重要)3:力扣股票购买时机难度中等其他滴也忘记了,因为拿到offer后鼠鼠一下子就落地了,脑子自动过滤掉可能会攻击鼠鼠的记忆😷~~~秒过深圳客服三面:项目大战参与战斗的人员有:成员1:表单封装及其底层原理,使用成本的优化,声明式表单成员2:公司内部库生命周期管理成员3:第三方库和内部库冲突如何源码断点调试并打补丁解决成员4:埋点的艺术成员5:线上项目捷报频传如何查出内鬼成员6:大文件分片的风流趣事成员7:设计模式对对碰成员8:我构建hooks应对经理的新增的小需求的故事可能项目回答的比较流利,笔试题3道,都很简单,相信大家应该都可以手拿把掐😇~~~过过过无hr面后续煎熬等待几天直接hr打电话发offer了,希望大家也可以拿到自己心仪的offer
法力无边年:牛哇,你真是准备得充分,我对你没有嫉妒,都是实打实付出
查看19道真题和解析
点赞 评论 收藏
分享
葬爱~冷少:我当时都是上午刷力扣,下午背八股,有活给我先别急,没活就干自己的事情
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务