Java高级开发必知必会——反射


title: Java高级开发必知必会——反射
author: rocklei123
tags:

  • Java
  • 反射
    categories: Java
    date: 2018-09-16 08:20:57

1.目标与意义

反射是Java开发中一个非常重要的概念,掌握了反射的知识,才能更好的学习Java高级课程。如Spring框架的核心就是使用Java反射实现的,而且对做一些Java底层的操作会很有帮助。

** 强烈建议大家手动敲一遍,会收获颇丰! **

** 目标:**

  • (1)、能够理解并应用反射机制。
  • (2)、能够了解反射机制在集合泛型中的应用。
  • (3)、有利于更好的学习框架等Java高级技术。

2.Class类的使用

  • (1) 在面向对象的世界里,万事万物皆对象。(java语言中,静态的成员、普通数据类型除外)

    ** 问题:** 类是不是对象呢?类是(哪个类的对象呢?)谁的对象呢?

    ** 答: ** 类是对象,类是java.lang.Class类的实例对象(类对象)

    • (2)这个对象到底如何表示(三种表示方法)

      • 通过隐藏的静态成员变量Student.class获取
      • 通过对象 stu1.getClass();方法获取
      • 通过Class.forName(类路径) 获取
package com.rocklei123.reflect;

public class ClassDemo1 {
    public static void main(String[] args) {
        //Student的实例对象如何表示
        Student stu1 = new Student();
        //Student 这个类 也是一个实例对象,Class类的实例对象,如何表示呢
        //任何一个类都是Class的实例对象,这个实例对象有三种表示方式

        //第一种表示方式--->实际在告诉我们任何一个类都有一个隐含的静态成员变量class
        Class c1 = Student.class;
        //第二中表达方式  已经知道该类的对象通过getClass方法
        Class c2 = stu1.getClass();


        //第三种表达方式
        Class c3 = null;
        try {
            c3 = Class.forName("com.rocklei123.reflect.Student");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //不管c1 or c2 or c3都代表了Student类的类类型,一个类只可能是Class类的一个实例对象.
        System.out.println(c1 == c2); //结果True
        System.out.println(c2 == c3);//结果True

        //我们完全可以通过类的类类型创建该类的对象实例---->通过c1 or c2 or c3创建Student的实例对象
        try {
            Student stu2 = (Student) c1.newInstance();//需要有无参数的构造方法
            stu2.print();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

class Student {
    void print() {
        System.out.println("I am a student");
    }
}

另外Class类对象不可以new 出来,因为它的构造方法为私有的,只有JVM 才可以创建类对象

    /*
     * Private constructor. Only the Java Virtual Machine creates Class objects.
     * This constructor is not used and prevents the default constructor being
     * generated.
     */
    private Class(ClassLoader loader) {
        // Initialize final field for classLoader.  The initialization value of non-null
        // prevents future JIT optimizations from assuming this final field is null.
        classLoader = loader;
    }

3.Java动态加载类

** 请大家区分编译、运行:** 编译时刻加载类是静态加载类、运行时刻加载类是动态加载类

  • (1) new 创建的对象,是静态类加载,在编译时刻就需要加载所有的可能使用到的类。

  • (2)Class.forName(“类的全称”)不仅表示了,类的类类型,还代表了动态加载类,在运行时刻加载。

4.图解类的正常加载过程和反射的原理在与class对象

图解类的正常加载过程和反射的原理在与class对象
在这里插入图片描述

图解类的加载过程中各对象创建时占用的JVM内存区域
在这里插入图片描述

5.基本的数据类型、void关键字的类类型

基本的数据类型、void等关键字等都存在类类型

package com.rocklei123.reflect;

public class ClassDemo2 {
    public static void main(String[] args) {

        Class c1 = int.class;//int 的类类型
        Class c2 = String.class;//String类的类类型   String类字节码(自己发明的)
        Class c3 = double.class;
        Class c4 = Double.class;
        Class c5 = void.class;
        Class c6 = Enum.class;

        System.out.println(c1.getName());
        System.out.println(c2.getName());
        System.out.println(c2.getSimpleName());//不包含包名的类的名称
        System.out.println(c3.getName());
        System.out.println(c4.getName());
        System.out.println(c5.getName());
        System.out.println(c6.getName());

        /**
         * 结果:
         * c1: int
         * c2: java.lang.String
         * c2: String
         * c3: double
         * c4: java.lang.Double
         * c5: void
         * c6: java.lang.Enum
         */
    }
}

6.通过反射获取方法信息、成员变量、构造函数信息

实体类信息:

package com.rocklei123.spring.transaction.transferAccount.entity;

/**
 * @Title: SpringTransactionTest
 * @Description:
 * @Author: rocklei123
 * @Date: 2018/8/14 14:52
 * @Version: 1.0
 */
public class User {
	private String username;
	private double money;

	public User(String username, double money) {
		this.username = username;
		this.money = money;
	}

	public User(){

	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public double getMoney() {
		return money;
	}

	public void setMoney(double money) {
		this.money = money;
	}
}

反射ClassUtil打印类的信息,包括类的成员函数、成员变量(只获取成员函数):

package com.rocklei123.reflect;

import com.rocklei123.spring.transaction.transferAccount.entity.User;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @ClassName: ClassUtil
 * @Auther: rocklei123
 * @Date: 2018/9/16 09:40
 * @Description: 打印类的信息,包括类的成员函数、成员变量(只获取成员函数)
 * @Version 1.0
 */
public class ClassUtil {

    /**
     * 打印类信息
     *
     * @param object
     */
    public static void printClassInfoMessage(Object object) {
        Class c = object.getClass();
        System.out.println("类加载器为:" + c.getClassLoader());
        System.out.println("包名为:" + c.getPackage());
        System.out.println("类名称为:" + c.getName());
        System.out.println("===========================================");
    }

    /**
     * 打印方法信息
     *
     * @param object
     */
    public static void printClassMethodMessage(Object object) {
        Class c = object.getClass();
        /*
         * Method类,方法对象
         * 一个成员方法就是一个Method对象
         * getMethods()方法获取的是所有的public的函数,包括父类继承而来的
         * getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
         */
        Method[] methods = c.getMethods();
        for (int i = 0; i < methods.length; i++) {
            Method method = methods[i];
            System.out.print("方法名称为:" + method.getName() + " ,");
            Class returnType = method.getReturnType();
            System.out.print(" 方法返回值类型为:" + returnType.getName() + " 方法参数类型(");
            Class[] parameterTypes = method.getParameterTypes();
            for (int j = 0; j < parameterTypes.length; j++) {
                System.out.print(parameterTypes[j].getName());
            }
            System.out.println(")");
            System.out.println("------------------------------------");
        }
        System.out.println("===========================================");
    }

    /**
     * 打印成员变量信息
     *
     * @param object
     */
    public static void pirntClassFiledsMessage(Object object) {
        /*
         * 成员变量也是对象
         * java.lang.reflect.Field
         * Field类封装了关于成员变量的操作
         * getFields()方法获取的是所有的public的成员变量的信息
         * getDeclaredFields获取的是该类自己声明的成员变量的信息
         */
        Class c = object.getClass();
        Field[] fields = c.getFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            Class fieldType = field.getType();
            String fieldName = field.getName();
            System.out.print("成员属性类型为:" + fieldType.getName() + ",属性名称为:" + fieldName + " , ");
        }
        System.out.println("===========================================");
    }

    /**
     * 打印构造方法信息
     *
     * @param object
     */
    public static void printConstructMessage(Object object) {
        /*
         * 构造函数也是对象
         * java.lang. Constructor中封装了构造函数的信息
         * getConstructors获取所有的public的构造函数
         * getDeclaredConstructors得到所有的构造函数
         */
        Class c = object.getClass();
        Constructor[] constructors = c.getConstructors();
        for (int i = 0; i < constructors.length; i++) {
            Constructor cons = constructors[i];
            System.out.print("构造方法:" + cons.getName() + "(");
            //获取构造函数的参数列表--->得到的是参数列表的类类型
            Class[] paramTypes = cons.getParameterTypes();
            for (Class class1 : paramTypes) {
                System.out.print(class1.getName() + ",");
            }
            System.out.println(")");
        }
        System.out.println("===========================================");
    }

    public static void main(String[] args) {
        User user = new User();
        ClassUtil.printClassInfoMessage(user);
        ClassUtil.printClassMethodMessage(user);
        ClassUtil.printConstructMessage(user);
    }
}

执行结果:

类加载器为:sun.misc.Launcher$AppClassLoader@4921a90
包名为:package com.rocklei123.spring.transaction.transferAccount.entity
类名称为:com.rocklei123.spring.transaction.transferAccount.entity.User
===========================================
方法名称为:getUsername , 方法返回值类型为:java.lang.String 方法参数类型()
------------------------------------
方法名称为:setUsername , 方法返回值类型为:void 方法参数类型(java.lang.String)
------------------------------------
方法名称为:getMoney , 方法返回值类型为:double 方法参数类型()
------------------------------------
方法名称为:setMoney , 方法返回值类型为:void 方法参数类型(double)
------------------------------------
方法名称为:wait , 方法返回值类型为:void 方法参数类型(longint)
------------------------------------
方法名称为:wait , 方法返回值类型为:void 方法参数类型(long)
------------------------------------
方法名称为:wait , 方法返回值类型为:void 方法参数类型()
------------------------------------
方法名称为:equals , 方法返回值类型为:boolean 方法参数类型(java.lang.Object)
------------------------------------
方法名称为:toString , 方法返回值类型为:java.lang.String 方法参数类型()
------------------------------------
方法名称为:hashCode , 方法返回值类型为:int 方法参数类型()
------------------------------------
方法名称为:getClass , 方法返回值类型为:java.lang.Class 方法参数类型()
------------------------------------
方法名称为:notify , 方法返回值类型为:void 方法参数类型()
------------------------------------
方法名称为:notifyAll , 方法返回值类型为:void 方法参数类型()
===========================================
构造方法:com.rocklei123.spring.transaction.transferAccount.entity.User(java.lang.String,double,)
构造方法:com.rocklei123.spring.transaction.transferAccount.entity.User()
===========================================

7.方法的反射

1)如何获取某个方法

方法的名称和方法的参数列表才能唯一决定某个方法

2)方法反射的操作

method.invoke(对象,参数列表)

package com.rocklei123.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @ClassName: MethodDemo1
 * @Auther: rocklei123
 * @Date: 2018/9/16 11:11
 * @Description: TODO
 * @Version 1.0
 */
public class MethodDemo1 {
    public static void main(String[] args) {
        Demo demo = new Demo();
        Class demoClass = demo.getClass();
    /*
		 * 2.获取方法 名称和参数列表来决定  
		 * getMethod获取的是public的方法
		 * getDelcaredMethod自己声明的方法
		 */

        try {
          //两种获取方式
          //Method m =  c.getMethod("("", new Class[]{int.class,int.class});
          //Method m = c.getMethod("add", int.class, int.class);
            Method addMethod = demoClass.getMethod("add", int.class, int.class);
            addMethod.invoke(demo, new Object[]{10, 20});
            Method printMethod = demoClass.getMethod("print", new Class[]{String.class, String.class});
            printMethod.invoke(demo, "hello", "world");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    /**
     * 结果:
     * sum=30
     * HELLO WORLD !
     */

}

class Demo {
    public int add(int a, int b) {
        int c = a + b;
        System.out.println("sum=" + c);
        return c;
    }

    public void print(String a, String b) {
        System.out.println(a.toUpperCase() + " " + b.toUpperCase() + " !");
    }
}

8.通过Class,Method来认识泛型的本质

  • 编译之后集合的泛型是去泛型化的
  • Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
  • 验证:我们可以通过方法的反射来操作,绕过编译
package com.rocklei123.reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName: MethodDemo2
 * @Auther: rocklei123
 * @Date: 2018/9/16 12:22
 * @Description: TODO
 * @Version 1.0
 */
public class MethodDemo2 {
    public static void main(String[] args) {
        List<Integer> listInt = new ArrayList<Integer>();
        List<String> listStr = new ArrayList<String>();
        listStr.add("Hello");
        Class classListInt = listInt.getClass();
        Class classListStr = listStr.getClass();
        System.out.println(classListInt == classListStr);
        /*
         * classListInt == classListStr 结果返回true说明编译之后集合的泛型是去泛型化的
         * Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
         * 验证:我们可以通过方法的反射来操作,绕过编译
         */

        try {
            Method method = classListStr.getMethod("add", Object.class);
            method.invoke(listStr, 20);
            System.out.println("listStr大小:" + listStr.size());
            System.out.println("listStr值:" + listStr);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        /**
         *  结果:
         *  true
         *  listStr大小:2
         *  listStr值:[Hello, 20]
         */
    }
}

9.参考

慕课网 https://www.imooc.com/learn/199

Java基础之—反射(非常重要) https://blog.csdn.net/sinat_38259539/article/details/71799078

10.建议大家手动敲一遍会收获颇丰!

11.欢迎关注米宝窝,持续更新中,谢谢!

米宝窝 https://rocklei123.github.io/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
 本次课程会带着大家学习Hash算法,从源码的角度去学习算法,更加容易理解的方式去学习,能够更高效的吸收学到的内容,也能培养出能够独自看源码,分析源码的能力。Hash,一般翻译“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。  哈希表是根据设定的哈希函数H(key)和处理冲突方法将一组关键字映射到一个有限的地址区间上,并以关键字在地址区间的象作为记录在表的存储位置,这种表称为哈希表或散列,所得存储位置称为哈希地址或散列地址。作为线性数据结构与表格和队列等相比,哈希表无疑是查找速度比较快的一种。  通过将单向数学函数(有时称为“哈希算法”)应用到任意数量的数据所得到的固定大小的结果。如果输入数据有变化,则哈希也会发生变化。哈希可用于许多操作,包括身份验证和数字签名。也称为“消息摘要”。  简单解释:哈希(Hash)算法,即散列函数。它是一种单向密码体制,即它是一个从明文到密文的不可逆的映射,只有加密过程,没有解密过程。同时,哈希函数可以将任意长度的输入经过变化以后得到固定长度的输出。哈希函数的这种单向特征和输出数据长度固定的特征使得它可以生成消息或者数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值