【javase-improved】第二篇:Java反射机制


🤣🤣写在前面:由于反射这一章是相对比较难的一章,要想彻底理解反射还需要以后长期的经验j积累
所以本篇文章主要基于代码层面对于一些反射的基本使用以及代码层面的一些理解进行总结概括。有什么理解不对的地方还希望各位佬们能够加以指正!👀👀

一.JAVA反射机制的概述

(1) 基于代码对于反射的理解

1. Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。(反射可以获得类中的任意一个属性即使这个属性是private修饰的可见反射的功能的强大之处)

2.加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可
以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看
到类的结构,所以,我们形象的称之为: 反射。

可能通过语言叙述还是不能很好的理解反射,what??,反射究竟怎么用?
在这里插入图片描述接下来我们通过代码层面的对比对反射进行一个初步的理解
在这里插入图片描述创建辅助的Person类

package 反射机制;

public class Person {

    private String name;
    public int age;

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    public Person(String name, int age) {

        this.name = name;
        this.age = age;
    }

    //私有构造器
    private Person(String name) {
        this.name = name;
    }

    public Person() {
        System.out.println("Person()");
    }

    public void show(){
        System.out.println("你好,我是一个人");
    }

    private String showNation(String nation){
        System.out.println("我的国籍是:" + nation);
        return nation;
    }
}

正常方式(直接通过new 进行创建对象,通过对象对公有的成员的进行修改)

//不用反射时的情况
    @Test
    public void test(){
       // 创建对象调用内部对象属性,但是不能调用私有变量的修饰的对象
        Person person=new Person("tom",18);

        person.age=19;

        person.show();
    }

反射的方式创建对象

 @Test
    public void test1() throws Exception{

        //通过反射创建 Person类对象
        //创建Class类对象,获得纵观整个类的权限
        Class clazz=Person.class;
  //调用指定的构造方法
         Constructor<Person>  cons=clazz.getConstructor(String.class,int.class);
         Person p=cons.newInstance("Tom",18);
        System.out.println(p);
        }
        

(1)通过反射访问类(以Person为例)内部成员

a. 访问public 修饰的成员(构造器,属性,访问)

@Test
    public void test() throws Exception{
       //访问public 修饰的属性
        //1.创建Class类的对象
        Class clazz =Person.class;
        //2.获取指定的公共的构造器
    Constructor cons =clazz.getConstructor(String.class,int.class);
    //3.根据构造器实例化对象
    Person p=(Person)cons.newInstance("Tom",18);

    Field age=clazz.getDeclaredField("age");
    age.set(p,20);
    System.out.println(p);
    //调用公共的方法
    Method method=clazz.getDeclaredMethod("show");
    method.invoke(p);

    }

结果
在这里插入图片描述

b. 访问private 修饰的成员(构造器,属性,访问)

 @Test
    public void test1() throws Exception {
        //访问public 修饰的属性
        //1.创建Class类的对象
        Class clazz =Person.class;
        //2.获取指定的公共的构造器
        Constructor cons =clazz.getDeclaredConstructor(String.class);
        cons.setAccessible(true);
        cons.newInstance("Tom");
        //3.根据构造器实例化对象
        Person p=(Person)cons.newInstance("Tom");

        Field name=clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(p,"Jack");
        System.out.println(p);

    }

由上面的代码不难总结除在对私成员进行访问的时候要注意一下几点:
1.一定要用setAcccessible(True)打开访问权限

2…在调用成员时
一般调用 getDeclaredXX…形式的方法

二.动态语言和静态语言的区分

1 、动态语言
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以
被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是 在运
行时代码可以根据某些条件改变自身结构

主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。
2 、静态语言
与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、
C++。

Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动
态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。(反射可以使JAVA看起来具有一定的动态性)

下面我们一个例子来体会一下java在反射机制下体现的动态性:


    //体会反射的动态性:运行时才知道classPath
    @Test
    public void test2(){

        for(int i = 0;i < 100;i++){
            int num = new Random().nextInt(3);//0,1,2
            String classPath = "";
            switch(num){
                case 0:
                    classPath = "java.util.Date";
                    break;
                case 1:
                    classPath = "java.lang.Object";
                    break;
                case 2:
                    classPath = "创建运行时类对象.Person";
                    break;
            }

            try {
                Object obj = getInstance(classPath);
                System.out.println(obj);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }



    }
    /*
       创建一个指定类的对象。
       classPath:指定类的全类名
        */
    public Object getInstance(String classPath) throws Exception {
        Class clazz =  Class.forName(classPath);
        return clazz.newInstance();
    }



}

上面的代码是实现对特定类创建对象,运用了switch case语句,由代码可知,只有代码运行起来才会知道创建那个类的对象,正好对应动态性的特点“在运
行时代码可以根据某些条件改变自身结构”

三.Java 反射机制功能研究

Java 反射机制提供的功能

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时判断任意一个类所具有的成员变量和方法
  4. 在运行时获取泛型信息
  5. 在运行时调用任意一个对象的成员变量和方法
  6. 运行时处理注解
  7. 生成动态代理

可见反射机制的功能是十分的强大的,我们在下文再对逐一的功能进行体现

四.Class类的理解与获取

(1)Class 类的理解

1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
2.换句话说,Class的实例就对应着一个运行时类。
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
在这里插入图片描述
通过上述的描述我们不难得出结论:既然被加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例,那么任何一个类都可以被提取为Class类的实例,因为任何一个类都会被执行并加载到内存中。
在这里插入图片描述

(2)Class的常用方法

方法名 功能说明
static Class forName(String name) 返回指定类名 name 的 Class 对象
Object newInstance() 调用缺省构造函数,返回该Class对象的一个实例getName()返回此Class对象所表示的实体(类、接口、数组类、基本类型
或void)名称

Class getSuperClass() 返回当前Class对象的父类的Class对象
Class [] getInterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Class getSuperclass() == 返回表示此Class所表示的实体的超类的Class==
Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
Field[] getDeclaredFields() 返回Field对象的一个数组
Method getMethod(Stringname,Class … paramTypes)
返回一个Method对象,此对象的形参类型为paramType
我们下面的模块会对这些方法进行一一体现。!

(3)获取Class类的常用放法

1. 直接.class进行获取

public void test() throws ClassNotFoundException {
        //一:调用运行时类的属性
        Class clazz = Person.class;
        System.out.println(clazz);
        }

2. 通过运行时类的对象调用getClass()

public void test() throws ClassNotFoundException {
		Person p=new Person();
		Class clazz1=p.getClass();
		System.out.println(clazz);
}

3.调用Class类的静态方法forName(classPath)

public void test() throws ClassNotFoundException {
        //三.调用Class类的静态方法forName(classPath)
        Class clazz2=Class.forName("反射机制.Person");
        Class clazz3=Class.forName("java.lang.String");
        System.out.println(clazz3);
}

4.使用类加载器:获取当前类的加载器

public void test() throws ClassNotFoundException {
 //方法四:使用类加载器:获取当前类的加载器
        ClassLoader classLoader=ReflectDemo1.class.getClassLoader();
        Class clazz4=classLoader.loadClass("反射机制.Person");
        System.out.println(clazz4);
        }

几种创建方式的结果是一样的

    System.out.println(clazz==clazz1);//true
    System.out.println(clazz==clazz2);//true

五.Class对象应用的类型范围

在这里插入图片描述

Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// 只要元素类型与维度一样,就是同一个Class
System.out.println(c10 == c11);

只要元素类型与维度一样,就是同一个Class

六.类的加载与类加载器(ClassLoader)的应用

1.类的加载过程

在这里插入图片描述

2.类加载的作用

在这里插入图片描述

在这里插入图片描述

3.类加载器的分类

在这里插入图片描述其中引导类加载器主要加载一些jvm再带的类
扩展类加载器主要加载一些jar包
系统类加载器主要加载一些自己定义的类

4.Java类编译、运行的执行的流程

在这里插入图片描述

5.ClassLoader的用法

   @Test
     public void test(){

        //1.获取系统类加载器
        ClassLoader cld=ClassLoaderDemo.class.getClassLoader();
        System.out.println(cld);
        //2.调用getParent()方法获得扩展类加载器
        ClassLoader c=cld.getParent();
        System.out.println(c);
        //调用扩展类加载器的getParent():无法获得引导类加载器
        //引导类记载器负责加载java的核心类库,无法调用自定义类
        ClassLoader c1=c.getParent();
        System.out.println(c1);
        //用java核心类库进行测试一下
        //有但是恰巧也是null
        ClassLoader c3=String.class.getClassLoader();
        System.out.println(c3);
    }

七.创建运行时类的对象

用反射(newInstance()方法)创建运行时类对象

@Test
    public  void test() throws IllegalAccessException, InstantiationException {
        Class<Person> clazz=Person.class;
 
        Person p=clazz.newInstance();
        System.out.println(p);

    }

newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。

要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常,设置为public。

    在javabean中要求提供一个public的空参构造器。原因:
    1.便于通过反射,创建运行时类的对象
    2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器

八.获取运行时类的基本结构

1.获取运行时类的指定属性

 @Test
    public void testField1() throws Exception {
        Class clazz = Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
        Field name = clazz.getDeclaredField("name");

        //2.保证当前属性是可访问的
        name.setAccessible(true);
        //3.获取、设置指定对象的此属性值
        name.set(p,"Tom");

        System.out.println(name.get(p));
    }

2.操作运行时类中的指定的方法

   @Test
    public void testMethod() throws Exception {

        Class clazz = Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        /*
        1.获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
         */
        Method show = clazz.getDeclaredMethod("show", String.class);
        //2.保证当前方法是可访问的
        show.setAccessible(true);

        /*
        3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
         */
        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
        System.out.println(returnValue);

        System.out.println("*************如何调用静态方法*****************");

        // private static void showDesc()

        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
//        Object returnVal = showDesc.invoke(null);
        Object returnVal = showDesc.invoke(Person.class);
        System.out.println(returnVal);//null

    }

3.调用运行时类中的指定的构造器

    @Test
    public void testConstructor() throws Exception {
        Class clazz = Person.class;

        //private Person(String name)
        /*
        1.获取指定的构造器
        getDeclaredConstructor():参数:指明构造器的参数列表
         */

        Constructor constructor = clazz.getDeclaredConstructor(String.class);

        //2.保证此构造器是可访问的
        constructor.setAccessible(true);

        //3.调用此构造器创建运行时类的对象
        Person per = (Person) constructor.newInstance("Tom");
        System.out.println(per);

    }

当然还可以获取运行时类中更丰富的方法,我们可以完善Person类,添加接口,注释,以及继承关系,然后通过反射获取其丰富的方法。
大家可以点击下方连接去我的代码仓库,创建运行时类包下一探究竟~
day 12 反射机制//创建运行时类对象

在这里插入图片描述

  • 9
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@Starry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值