Java--反射机制-Reflection

共 21825字,需浏览 44分钟

 ·

2021-03-26 10:41

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

76套java从入门到精通实战课程分享

概述

动态语言:

运行时可以在某些情况下改变自身结构的语言(一般为使用字符串改变)


例如JS语句:

var x="var a=1;var b=2;alert(a+b);";
eval(x);

相当于:

var x="var a=1;var b=2;alert(a+b);";
var a=1;
var b=2;
alert(a+b);

主要的有:C#,JavaScript,PHP,Python,ObjextC等


静态语言:

主要的有:JAVA,C,C++,但是JAVA能通过反射机制获得一定的动态性,类似动态语言的特性


反射简介:

Class c= Class.forName("java.lang.String");

区别:

正常:

import 包名
psv  main(){
    类名 变量名=new 类名()//实例化一个对象
    
}

加载完类之后,就有一个Class类型的对象(类模板,一个类只有一个),包含了完整类的结构信息


结构信息和静态放在—方法区


类模板—堆区


反射:

由实例化对象—》使用getClass()方法得到Class类模板—》包名


提供的运行时(Runtime)功能:

(1)判断对象所属的类

(2)构造任意类的对象

(3)判断任意类具有的成员变量和方法

(4)调用任意类具有的成员变量和方法

(5)获取泛型信息

(6)处理注解

(7)动态代理


主要涉及到JDK的包:

java.lang.Class   类模板
java.lang.Method  类的方法
java.lang.Field   成员变量
java.lang.Contructor 构造器
等等-----

获取反射对象

首先我们先自己定义一个需要反射的对象

class User{
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.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;
    }

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

}

然后我们初试一下如何获取Class模板:

/**
 * @author paakciu
 * @ClassName: test01
 * @date: 2021/3/21 11:21
 */
public class test01 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1=Class.forName("reflection.User");
        System.out.println(c1);
    }
}

结果:


一个类只有一个Class模板,在jVM的课上我们已经讲过了,这里不再重复试验,有兴趣的小伙伴自行弄多几个Class,判断是不是同一个


源码浅谈


forName里面调用了两个方法,进去之后都是native的,不深究。


获取Class的方式

Class c1=User.class;
Class c2=user1.getClass();
Class c3=Class.forName("包名.User");
long.class
基础类型可以用  类名.Type 如Long.TYPE= (Class<Long>) Class.getPrimitiveClass("long");
还有通过ClassLoader 类加载初始化的时候会产生

public class test02 {
    public static void main(String[] args) {
        Person person=new Student();
        //方式1 对象获取
        Class c1 = person.getClass();
        System.out.println(c1);
        //方式2 forname获取
        try {
            Class c2=Class.forName("test.Student");
            System.out.println(c2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //方式3 通过类名.class 获取
        Class c3= Student.class;
        System.out.println(c3);

        //基本类型也有类名
        Class c4=Integer.TYPE;
        System.out.println(c4);
        //父类类型
        Class c5=c1.getSuperclass();
        System.out.println(c5);

    }
}
class Person{
    String name;
    int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.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;
    }
}

class Student extends Person{
    public Student(){
        this.name="nihao";
    }
}

所有类型的Class对象


结果:



类加载分析

堆:存放new的对象和数组,所有线程共享


栈:存放基础类型的变量和引用


方法区:存放Class结构信息和静态变量,方法模板,所有线程共享


类加载的过程:

加载Load—》链接link—》初始化initialize


1、创建一个Class,静态数据转化成方法区的运行时数据结构

2、二进制链接到jvm中,

     验证:进行安全验证/规范检查

     准备:正式为static变量分配内存并默认初始化阶段

     解析:常量池符号引用转换成直接引用(内部把变量名映射成内存地址)

3、JVM负责初始化

     执行类构造器()方法,由编译器自动收集类中所有的类变量赋值动作和静态代码块中的语句合并产生,注意此处是类构造器并不是对象构造器,用于构造类信息

     当初始化一个类时,发现父类并没有初始化,会先进行父类初始化

    虚拟机自动在该过程加锁同步。

public class test03 {
    public static void main(String[] args) {
        loadtest l=new loadtest();
        System.out.println(l.a2);
    }
}
class loadtest{

    
}

练习:

public class Main {


    public static void main(String[] args) {
        System.out.println("A");
        new Main();
        new Main();
    }


    public Main() {
        System.out.println("B");
    }


    {
        System.out.println("C");
    }


    static {
        System.out.println("D");
    }
}
以上程序输出的结果,正确的是?


答案:DACBCB


分析类初始化


/**
 * test.test04
 *
 * @author paakciu
 * @date 2021/1/13 16:11
 */
public class test04 {
    static {
        System.out.println("Main's class的 static 加载");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        //主动引用,会发生类初始的
        //1.实例化对象,先加载父类的static
        //test.Son s=new test.Son();
        //2.调用静态成员(除了final)包括属性和方法
        //test.Son.functionson();
        //System.out.println(test.Son.b);
        //3.使用反射调用
        //Class aClass = Class.forName("test.Son");

        //被动引用,不会发生类的初始化
        //1.调用父类的静态变量,不会导致子类初始化
        //System.out.println(test.Son.a);
        //2.数组声明类,不会触发(因为没有实际上的实例化)
        //test.Son[] sons=new test.Son[10];
        //3.引用常量,不会触发(会被存入常量池,链接阶段)
        //System.out.println(test.Son.c);

        //容易混淆的点
        //其实下面两种情况也是被动引用1,3的情况。因为不是进入常量池的对象。
        //带有实例化的,子类的静态变量,会导致子类,父类都加载
        //System.out.println(test.Son.d);
        //带有实例化的父类静态变量,只有父类加载
        //System.out.println(Son.e);
        //区别于没有实例化的变量
        //System.out.println(Son.f);
    }
}

class Father{
    static int a=2;
    static final String e=new String("e");
    static String f="f";

    Father(){
        System.out.println("父类的构造方法");
    }
    static{
        System.out.println("父类static加载");
    }
    public static void functionfather(){
        System.out.println("调用父类静态方法");
    }
}
class Son extends Father{

    static int b=2;
    static final int c=2;
    static final String d=new String("d");

    Son(){
        System.out.println("子类的构造方法");
    }
    static {
        System.out.println("子类static加载");
    }
    public static void functionson(){
        System.out.println("调用子类静态方法");
    }
}

类加载器

JVM已经提过了



获取类的运行时结构

public class test05 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class clazz=Class.forName("test.User");

        //获取类的名称
        System.out.println("=====类的名称=====");
        System.out.println(clazz.getName());//全路径名称
        System.out.println(clazz.getSimpleName());//获得类名

        //获取类的属性
        System.out.println("=====类的属性=====");
        //包含父类的公开属性
        System.out.println("公开属性:");
        Field[] fields=clazz.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
        //包含所有权限的属性,但是不包含父类的属性
        System.out.println("该类模板(不包括父类)的所有属性:");
        Field[] fields2=clazz.getDeclaredFields();
        for (Field field : fields2) {
            System.out.println(field);
        }
        System.out.println("指定属性:");
        Field f=clazz.getField("name");
        System.out.println(f);
        f=clazz.getDeclaredField("name");
        System.out.println(f);

        //获取类的方法
        System.out.println("=====类的方法=====");
        //包含父类的公开方法
        System.out.println("公开方法:");
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("该类模板(不包括父类)的所有方法:");
        methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        //获取构造器
        System.out.println("=====类的构造器=====");
        System.out.println("公开构造器:");
        Constructor[] constructors=clazz.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("该类模板(不包括父类)的所有构造器:");
        constructors=clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        System.out.println("指定构造器:");
        System.out.println(clazz.getDeclaredConstructor(int.class));

    }
}
class UserFather{
    public String namefather;
    public void funcfather(){
        System.out.println("father");
    }
    public UserFather(){ }

}
class User extends UserFather{
    public String name;
    private int age;
    public User(){ }
    private User(int x){ }
    public void func1(){
        System.out.println("user1");
    }
    private void func2(){
        System.out.println("user2");
    }
}

动态创建对象执行方法

public class test06 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获取Class对象
        Class clazz=Class.forName("test.test06User");
        System.out.println(clazz);

        //构造一个对象
        test06User test=(test06User) clazz.newInstance();

        //指定构造器
        Constructor constructor=clazz.getDeclaredConstructor(String.class,int.class,Integer.TYPE);
        test06User test2=(test06User)constructor.newInstance("paakciu",10,6);
        System.out.println(test2);

        //通过反射获取指定方法-----方法名,参数列表
        System.out.println("通过反射获取指定方法=======================");
        Method method1=clazz.getDeclaredMethod("setName",String.class);//
        method1.invoke(test2,"nihao");
        System.out.println("通过反射调用setName后:"+test2);


        //通过反射操作属性
        System.out.println("通过反射操作属性=======================");
        Field field1=clazz.getDeclaredField("age");

        //暴力!私有属性
        field1.setAccessible(true);
        field1.set(test2,20);
        System.out.println("通过反射设置age后:"+test2);
    }
}
class test06User{
    public String name;
    public int id;
    private int age;

    public test06User() {
        System.out.println("调用了无参构造器");
    }

    public test06User(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
        System.out.println("调用了有参构造器");
    }

    public String getName() {
        return name;
    }

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

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

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

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

性能对比(时间)

获取泛型信息(自行了解)



————————————————

版权声明:本文为CSDN博主「诠释那一刻」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/asd649433749/article/details/115054745




粉丝福利:Java从入门到入土学习路线图

👇👇👇

👆长按上方微信二维码 2 秒


感谢点赞支持下哈 

浏览 36
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报