反射(Reflection)(2月26日总结)

本文详细介绍了Java反射机制,包括Class类的理解、获取Class实例、类的加载与ClassLoader、创建运行时类的对象、获取类的完整结构及调用指定结构。重点讲述了反射在动态创建对象、访问私有结构和实现动态代理等方面的应用。强调了反射的动态性和在开发中的适用场景。
摘要由CSDN通过智能技术生成


尚硅谷代码视频:day11_反射
学习时长:1天

一、java反射机制概述

学习思路(B站尚硅谷)

反射之前,类的实例化
使用反射,实现同上的操作
反射的强大之处,调用类的私有结构
反射和封装性的对比

两个问题

//疑问1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中到底用那个?
        //建议:直接new的方式。
        //什么时候会使用:反射的方式。 反射的特征:动态性(登录或者注册)
//疑问2:反射机制与面向对象中的封装性是不是矛盾的?如何看待两个技术?
        //不矛盾。封装性(私有的用不着,可能是被公有的调用)
        //封装性:建议是否调用。反射:能否调用。

二、理解Class类并获取Class实例(重要)

二、理解Class类并获取Class实例(重要)
(正式认识反射)(反射难理解:在于动态性)

Class类的理解
1、加载到内存中的类,我们就称为运行时类,就作为Class的一个实例(加载到内存中的类本身,加上.calass属性,充当大Class的实例)
以前通过类去造对象,现在,类本身也是对象,大Class的一个对象。
万事万物皆对象?对象.xxx,File,URL,反射,前端(一对一对的标签)、数据库操作(一条一条的记录)

2.换句话说,Class的实例就对应着一个运行时类。
3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类

获取Class实例的4种方式
推荐使用第3种,如下
调用Class的静态方法:forName(String classPath)
Class clazz3 = Class.forName(“com.atguigu.java.Person”);

        //方式一:调用运行时类的属性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);

        方式三:调用Class的静态方法:forName(String classPath)
        使用最多,更好的体现了动态性
        Class clazz3 = Class.forName("com.atguigu.java.Person");
//        clazz3 = Class.forName("java.lang.String");
        System.out.println(clazz3);

        //Class的实例就对应着一个运行时类,加载到内存中会缓存一段时间,以上只是通过不同的方式获取到
        System.out.println(clazz1 == clazz2);//true
        System.out.println(clazz1 == clazz3);//true

哪些类型可以有Class对象?
外部类、成员(成员内部类、静态内部类)、局部内部类、匿名内部类
interface接口、[]数组、enum枚举、annotation注解@interface
primitive type基本数据类型

三、类的加载与ClassLoader的理解

理解类的加载过程、ClassLoader的理解
类的加载

类加载器

使用ClassLoader加载配置文件

Properties:用来读取配置文件
	@Test
    public void test2() throws Exception {

        Properties pros =  new Properties();
        //此时的文件默认在当前的module下。
        //读取配置文件的方式一:
        FileInputStream fis = new FileInputStream("jdbc.properties");
//        FileInputStream fis = new FileInputStream("src\\jdbc1.properties");
        pros.load(fis);

        //读取配置文件的方式二:使用ClassLoader
        //配置文件默认识别为:当前module的src下
//        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
//        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
//        pros.load(is);

        String user = pros.getProperty("user");
        String password = pros.getProperty("password");
        System.out.println("user = " + user + ",password = " + password);
    }

四、创建运行时类的对象(重要)

四、创建运行时类的对象(重要)

有了大Class实例后,可以干什么事呢?

第1件事:通过反射创建对应的运行时类的对象

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

99%情况下这么使用(.newInstance()就可以)
调用运行时类中的指定的构造器,只在特定情况下使用。

Class clazz = Person.class;
Person obj = clazz.newInstance();

		//Class是一个源头,必须先要有Class
                //类的泛型决定了newInstance()方法的返回值,省略了做强转
        Class<Person> clazz = Person.class;
		
		//造的就是Person类对象
		/newInstance():调用此方法,创建对应的运行时类(这里为Person类)的对象。内部调用了运行时类的空参的构造器。
        Person obj = clazz.newInstance();
        System.out.println(obj);

体会反射的动态性

			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 = "com.atguigu.java.Person";
                    break;
            }
            
			Class clazz =  Class.forName(classPath);
			System.out.println(clazz.newInstance());

5次输出结果:
Sun Feb 28 11:50:59 CST 2021
java.lang.Object@573fd745
Person()
Person{name='null', age=0}
Person()
Person{name='null', age=0}
Person()
Person{name='null', age=0}

五、获取运行时类的完整结构

五、获取运行时类的完整结构

有了Class的实例,比如c1,就指向了方法区中类的实体本身
实体类中有构造器、属性、方法;也有类所在的包、实现的接口、父类;方法可以有注解、权限修饰符、返回值类型、方法名、异常等
通过反射可以拿到以上结构
不要求掌握,了解就可以,实际开发不会这么做。感受反射的强大。

获取当前运行时类的属性结构
getFields():获取当前运行时类及其父类中声明为public访问权限的属性
getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)

    @Test
    public void test1(){

        Class clazz = Person.class;

        //获取属性结构
        //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for(Field f : fields){
            System.out.println(f);
        }
        System.out.println();

        //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for(Field f : declaredFields){
            System.out.println(f);
        }
    }

输出结果:
public int com.atguigu.java1.Person.id
public double com.atguigu.java1.Creature.weight

private java.lang.String com.atguigu.java1.Person.name
int com.atguigu.java1.Person.age
public int com.atguigu.java1.Person.id

获取运行时类的方法结构
getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)

还可以获取:
@Xxxx
权限修饰符 返回值类型 方法名(参数类型1 形参名1,…) throws XxxException{}

获取运行时类的构造器
getConstructors():获取当前运行时类中声明为public的构造器
getDeclaredConstructors():获取当前运行时类中声明的所有的构造器

六、调用运行时类的指定结构(重要)(开发时做)

六、调用运行时类的指定结构(重要)(开发时做)

调用运行时类的指定属性、指定方法、指定构造器
1.获取指定的某个方法
getDeclaredMethod():参数1 :指明获取的方法的名称 参数2:指明获取的方法的形参列表
Method show = clazz.getDeclaredMethod(“show”, String.class);

注解练习中使用:Method targetMethod = Cls.getDeclaredMethod(“method”);

2.保证当前方法是可访问的
show.setAccessible(true);
3. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
invoke()的返回值即为对应类中调用的方法的返回值
show.invoke(p,“CHN”); //我的国籍是:CHN

    /*
    如何操作运行时类中的指定方法 -- 需要掌握
     */
    @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()的返回值即为对应类中调用的方法的返回值。
         */
        //invoke调用的意思
        //这里常用,需要熟悉
        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN"); //我的国籍是:CHN
        System.out.println(returnValue);//CHN

        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

    }

七、反射的应用:动态代理

以上!!!
后续再总结!!!


总结(终版)
反射相关的主要API

java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器

属性class作为Class的实例
Class clazz = Person.class;

得到构造器
Constructor cons = clazz.getConstructor(String.class, int.class);
通过构造器造对象
Object obj = cons.newInstance("TOM", 12);
Person p = (Person) obj;
System.out.println(p.toString());

输出:Person{name='TOM', age=12}
在这里插入代码片
/*
    配置文件:
        放数据库配置信息,jdbc即数据库驱动
        (专门用于连接数据库:主机、端口号、用户名、密码)(即hostName,userName,port,password)

        第三方服务的账号信息 如:appKey , 秘钥
 */

    //AM    11:08讲配置文件
    新建一个配置文件:对应package--->new--->Resource Bundle就可以

    因为通过创建Properties对象加载配置信息(为什么配置文件以properties结尾?)
    

在这里插入图片描述

获取Class对象方法1

这就拿到数据库中的配置信息,可以用jdbc连接数据库
为什么把这些信息放到文件中?       方便修改:只修改一个地方,很方便
为什么讲配置文件?     为了说明第一种获取Class对象的方式是比较好的

获取Person当中的构造方法

//AM 11:35
    //建一个Person类
    //成员变量、构造方法、成员方法分别设置了不同的访问权限
    //用反射试一下
    此处省略Person类的定义

/*
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
 */
package com.cskaoyan.constructer;

import java.lang.reflect.Constructor;

/*
Constructor[] getConstructors()
Constructor[] getDeclaredConstructors()
 */
public class Demo {
    public static void main(String[] args) throws ClassNotFoundException {
        // 获取字节码文件对象personCls
                //Class 类的实例表示正在运行的 Java 应用程序中的类和接口
                //建议copy reference        com.cskaoyan.domain.Person
        Class personCls = Class.forName("com.cskaoyan.domain.Person");

        // 获取构造方法
                // getConstructors()    返回一个包含某些 Constructor 对象的数组,
                // 这些对象反映此 Class 对象所表示的类的所有 公共 构造方法。
        Constructor[] constructors = personCls.getConstructors();

        System.out.println("获取所有public的构造方法------------");
        for (Constructor c : constructors) {
            System.out.println(c);
        }

        System.out.println("获取所有构造方法-------------");
                // getDeclaredConstructors()    返回 Constructor 对象的一个数组,
                // 这些对象反映此 Class 对象表示的类声明的所有构造方法。
        Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor c : declaredConstructors) {
            System.out.println(c);
        }

    }
}

获取Person当中的成员变量

/*
Field[] getFields()
Field[] getDeclaredFields()
 */

在这里插入图片描述

在这里插入图片描述

获取Person当中的成员方法

// 获取所有成员方法 Method 用来描述我们的成员方法
                // Method[] getMethods()
                //Method[] getDeclaredMethods()

在这里插入图片描述
在这里插入图片描述

获取字节码文件对象
        Class personCls = Class.forName("com.cskaoyan.domain.Person");

		System.out.println("获取所有成员方法----------");
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method m : declaredMethods) {
            System.out.println(m);
        }

		获取所有成员方法----------
		private void com.cskaoyan.domain.Person.eat(java.lang.String)
		public void com.cskaoyan.domain.Person.eat()
		
		System.out.println("获取单个private的成员方法--------");
        Method eatMethod2 = personCls.getDeclaredMethod("eat", String.class);
        System.out.println(eatMethod2);
		
		获取单个private的成员方法--------
		private void com.cskaoyan.domain.Person.eat(java.lang.String)
		
		
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lacrimosa&L

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

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

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

打赏作者

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

抵扣说明:

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

余额充值