Java Day18

Java

day18_2023.9.1

反射

反射 :Java中的反射,用来将类中的方法或者属性、构造方法直接获取到,完成它们的调用

反射是框架设计的灵魂

学习反射之前,了解两个概念 : 编译期 和 运行期

编译期 : 指的是将源码,交给编译器,编译成计算机可以执行的文件的过程。在Java中就是把java文件编译成.class文件的过程。编译期就是做了一些翻译的功能,还没有将代码在内存中运行。

运行期 : 把编译后的文件交给计算机,直到程序结束。其实就是把在磁盘中的代码加载到内存中,执行起来

反射机制: 在运行期,对于任意的类,都能够获取这个类的所有属性和方法,这种动态获取信息以及动态调用方法的功能,称为Java的反射机制

Java代码在计算机中运行的三个阶段
在这里插入图片描述
反射的好处 :

1,可以在程序的运行过程中,获取到Class类对象中封装的对象,可以操作这些对象

2,可以 解耦,提高程序的可扩展性

获取Class类对象的方式

方式1: 在第一个阶段,还没有加载的时候

Class.forName(“全类名地址”) : 将字节码文件直接加载进内存,返回Class对象,一般将来用于配置文件,读取文件,加载类

**方式2:**通过类名的属性 class获取

类名.class的方式获取到这个类的类对象

方式3: 通过实例化后的对象获取

对象.getClass() : 获取到这个类的类对象

public class TestPerson {

    public static void main(String[] args) throws ClassNotFoundException {

        Person person = new Person();

        //方式1 : 通过Class.forName()
        Class personClass1 = Class.forName("com.iweb.airui369.test.Person");

        //方式2 : 通过类名.class
        Class personClass2 = Person.class;

        //方式3: 通过对象.getClass()
        Class personClass3 = person.getClass();
        //比较3个获取的类对象,值都是相同的,表示指向内存中的同一个地址
        //其实就是同一个类对象
        //得出结论 :
            //同一个字节码文件(xxx.class)在同一个程序的运行过程中,
            //只会被加载一次,可以通过不同的方式去找到这个类对象,
        //不管通过哪种方式,获取到的类对象都是同一个
        System.out.println(personClass1 == personClass2);
        System.out.println(personClass2 == personClass3);

    }
}


public class Person {
    //属性
    public int sid;
    private String name;
    private int age;
    //构造函数
    public Person() {
    }
    //成员方法
    public void eat(){}
}

Class类对象中,封装的其他几个对象

Field类 :Field类提供有关类或接口的单个字段的信息和动态访问。 反射的字段可以是类(静态)字段或实例字段。

Constructor类 :Constructor提供了一个类的单个构造函数的信息和访问。

Class类对象的常用方法(功能)

获取成员变量们

Field getField(String name)

返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。

Field[] getFields()

返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象。

Field getDeclaredField(String name)

返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。

Field[] getDeclaredFields()

返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。

 //获取成员变量相关的方法
        //Field getField(String name) 获取指定的public的属性
        Field sid = personClass1.getField("sid");
        System.out.println(sid);

        //Field[] getFields() 获取所有public的属性
        Field[] fields = personClass1.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("----------------");
        //Field getDeclaredField(String name)  获取所有(包含私有化)的指定的属性
        Field name = personClass1.getDeclaredField("name");
        System.out.println(name);
        System.out.println("----------------");
        //Field[] getDeclaredFields() 获取所有的属性
        Field[] fields1 = personClass1.getDeclaredFields();
        for (Field field1 : fields1) {
            System.out.println(field1);
        }
        //通过Field对象,完成对象属性值的获取和赋值
        System.out.println("----------------");
        Person p = new Person();
        p.sid = 1001;
        //获取p对象的sid属性值
        Object o = sid.get(p);
        System.out.println(o);
        //通过sid属性对象,完成person对象的sid属性赋值
        sid.set(p,1002);
        System.out.println(p.sid); //1002

通过暴力反射,完成私有属性的赋值

	//通过Field对象,结合setAccessible()方法对私有属性的赋值或者获取值
        System.out.println("----------------");
        name.setAccessible(true);  //暴力反射
        name.set(p,"jack");  //反射给属性赋值
        Object o1 = name.get(p);  //反射获取属性值
        System.out.println(o1);
获取构造方法们

Constructor getConstructor(类<?>… parameterTypes)

返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 类函数。

Constructor<?>[] getConstructors()

返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 类对象。

Constructor getDeclaredConstructor(类<?>… parameterTypes)

返回一个 Constructor对象,该对象反映 Constructor对象表示的类或接口的指定 类函数。

Constructor<?>[] getDeclaredConstructors()

返回一个反映 Constructor对象表示的类声明的所有 Constructor对象的数组 类 。

public class ConstructorDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        //获取类对象
        Class<Person> personClass = Person.class;

        //获取构造函数对象
        //Constructor<T> getConstructor(T<?>... parameterTypes)
        //传入构造函数的参数的类型,获取指定类对象的无参构造
        Constructor<Person> constructor = personClass.getConstructor();
        Person person = constructor.newInstance();
        System.out.println(person);
        //获取指定类对象的有参构造
        Constructor<Person> constructor1 = personClass.getConstructor(String.class, int.class);
        Person person1 = constructor1.newInstance("jack",20);
        System.out.println(person1);
        //Constructor<?>[] getConstructors()  获取所有的public的构造函数
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor2 : constructors) {
            System.out.println(constructor2);
        }
        //Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)

        Constructor<Person> constructor3 = personClass.getDeclaredConstructor(String.class);
        System.out.println(constructor3);
        //可以通过暴力反射,完成私有构造方法的调用,并且可以完成属性赋值
        constructor3.setAccessible(true);
        Person person2 = constructor3.newInstance("tom");
        System.out.println(person2);

        //类对象可以通过newInstance()方法,直接完成对象的创建
        Person person3 = personClass.newInstance();
        System.out.println(person3);

    }
}

使用反射完成对象创建和属性赋值

//创建一个Student对象,有3个私有属性,学号、姓名、班级,有一个toString()方法
//利用反射,创建1个无参Student对象,利用反射,给这个Student对象的属性赋值
//最后,将这个Student对象输出

public class Student {
    private int sid;
    private String name;
    private String className;

    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", name='" + name + '\'' +
                ", className='" + className + '\'' +
                '}';
    }
}

public class StudentTest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        //获取类对象
        Class<Student> studentClass = Student.class;

        //通过类对象调用方法
        Student s1 = studentClass.newInstance();

        //通过类对象获取属性
        Field sid = studentClass.getDeclaredField("sid");
        sid.setAccessible(true);
        Field name = studentClass.getDeclaredField("name");
        name.setAccessible(true);
        Field className = studentClass.getDeclaredField("className");
        className.setAccessible(true);

        //给指定的对象属性赋值
        sid.set(s1,1001);
        name.set(s1,"jack");
        className.set(s1,"1班");
        System.out.println(s1);

        //创建另一个对象
        Student s2 = studentClass.newInstance();
        sid.set(s2,1002);
        name.set(s2,"tom");
        className.set(s2,"2班");
        System.out.println(s2);
    }
}
获取成员方法们

Method getDeclaredMethod(String name, 类<?>… parameterTypes)

返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象。

Method[] getDeclaredMethods()

返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。

Method getMethod(String name, 类<?>… parameterTypes)

返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。

Method[] getMethods()

返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明

public class MethodDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

        //Method getMethod(String name, Class<?>... parameterTypes)
        //返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 类对象。
        //name参数表示 :方法名
        //Class<?>... parameterTypes :参数类.class
        //... 表示可变长的参数列表
        //创建类对象
        Class<Student> studentClass = Student.class;

        //通过类对象获取无参数方法
        Method sleep = studentClass.getMethod("sleep");
        //创建对象
        Student s1 = studentClass.newInstance();
        //通过Method类中的方法,完成对象对方法的调用
        sleep.invoke(s1);

        //通过类对象获取有参数方法
        Method eat = studentClass.getMethod("eat", String.class);
        eat.invoke(s1,"鸡腿");

        Method add = studentClass.getMethod("add", int.class, int.class);
        add.invoke(s1,10,20);

    }
}


public class Student {
    private int sid;
    private String name;
    private String className;

    public void sleep(){
        System.out.println("学生睡觉!");
    }
    public void eat(String food){
        System.out.println("学生在吃" + food);
    }
    public void add(int a,int b){
        System.out.println(a + b);
    }


    @Override
    public String toString() {
        return "Student{" +
                "sid=" + sid +
                ", name='" + name + '\'' +
                ", className='" + className + '\'' +
                '}';
    }
}

获取类名

         String className = studentClass.getName();
        System.out.println(className);
总结

获取类对象 :类名 .class

类对象常用方法

获取私有属性对象Field :Field getDeclaredField(String name)

获取构造方法对象Constructor :Constructor getConstructor(Class<?>… parameterTypes)

获取普通方法对象Method :Method getMethod(String name, Class<?>… parameterTypes)

几个对象的常用方法:

Filed类 :

setAccessible(true) 开启暴力反射,可以操作私有属性

get() 获取属性值

set(Object obj, Object value) 设置属性值

Constructor类:

newInstance(Object… initargs) 用来构建对象

如果是调用无参构造来创建,直接通过类对象调用 newInstance() 方法

Method类

invoke(Object obj, Object… args) 执行方法

getName() 获取方法名

模拟框架案例

需求 :写一个框架,将来再不改变任何代码的情况下,可以帮助我们创建任意类的对象,并且能够执行其中的任意方法。

properties配置文件

className=com.iweb.airui369.reflecttest2.Student
methodName=sleep
public class Person {
    public void eat(){
        System.out.println("eat.....");
    }
}


public class Student {
    public void sleep(){
        System.out.println("sleep.....");
    }
}


public class TestReflect {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //Person person = new Person();
        //person.eat();
        //Student student = new Student();
        //student.sleep();
        //需要在不改变任何代码的情况下,可以创建对象,并执行方法
        //把类和方法的信息,配置在一个配置文件中,通过代码读取到配置文件中的信息
        //完成加载和调用

        //1,在src目录下,创建一个info.properties 配置文件,在文件中写入需要加载的
        //类的信息和方法的信息

        //完成配置文件加载
        Properties properties = new Properties();
        //获取src目录下的配置文件,使用inputStream完成文件加载
        InputStream is =
                TestReflect.class.getClassLoader().
                        getResourceAsStream("info.properties");
        //properties对象加载is流对象中的数据
        properties.load(is);

        //去获取到properties对象中的配置数据信息
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");
       //获取类对象
        Class cls = Class.forName(className);
        //创建一个对象
        Object o = cls.newInstance();
        //获取方法对象
        Method method = cls.getMethod(methodName);
        //执行方法
        method.invoke(o);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值