首页 > 编程语言 >java 反射 Reflection

java 反射 Reflection

时间:2022-11-24 22:12:27浏览次数:52  
标签:lang 反射 java Reflection public 加载 annotation String

一.什么是反射:

Relection(反射):是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

公式:

Class  c  =  Class.forName("java.lang.String");     // 取得那个类或对象

反射好像一面镜子,我们可以通过镜子看到这个类的完整结构,所以叫反射

正常方式:

引入需要的包名    ----》  通过new关键字实例化    ----》  取得实例化对象

反射方式:

实例化对象   ----》getClass()方法  ----》得到完整的“包类”名称

Java反射的优点和缺点

优点:可以实现动态创建对象和编译,体现出很大的灵活性

缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且满足我们的要求,这类操作总是慢于直接执行相同操作,比如new对象方面

代码实现:

 

结果:

 

 

结论:一个类在内存中只有一个class对象

          一个类被加载后,类的整个结构都会被封装在class对象中

实质上我们也可以将反射理解为通过对象求解我们的类名 

 

二.那些对象可以拥有Class对象

 

class:外部类,成员类,局部内部类,匿名内部类

interface:接口

[  ]:数组

enum:枚举

annotation:注解@interface

primitive  type:基本数据类型

void


public static void main(String[] args) {
Class c1=Object.class; // 类
Class c2=Comparable.class; // 接口
Class c3= String[].class; //一维数组
Class c4=int[][].class; //二维数组
Class c5=Override.class; // 注解
Class c6= ElementType.class;// 枚举
Class c7=Integer.class; //基本数据类型
Class c8=void.class; //void
Class c9=Class.class; //class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}


 

输出结果

 

 

三.类加载与ClassLoader

将class文件内容加载到内存中,并将这些静态数据转换为方法区运行的数据结构,然后生成一个代表这个类的java.lang.Class对象

链接:将Java类的二进制代码合并到JVM运行时的状态之中的过程。

  1. 验证:确保加载的类信息符合JVM规范,没有安全方面问题
  2. 准备:正式为类变量(static)分配内存并设置默认值,这些内存都在方法区进行分配
  3. 解析:虚拟机常量池内的符号引用替换为直接引用地址的过程

初始化:

  • 执行类构造器<clinit>()方法的过程,该方法的作用是,自动将所有的类变量赋值动作,和静态代码块语句合并产生。
  • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要触发其父类进行赋值
  • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步

 

 结果:

四.类的初始化

什么时候会发生类的初始化呢

类的主动引用就一定会发生初始化

  1. 当虚拟机启动时,先初始化main方法所在类
  2. new 一个类的对象
  3. 调用类的静态成员和静态方法
  4. 使用java.lang.reflect包的方法对类进行反射调用
  5. 当初始化一个类的时候,如果其父类没有被初始化,会优先初始化它的父类

类的被动引用(不会发生类的初始化):

  1. 当访问一个静态域的时候,只有真正声明这个域的类才会被初始化。例如,通过子类去拿父类的静态变量的时候,不会导致子类的初始化
  2. 通过数组定义的类的引用,不会触发此类的初始化
  3. 引用常量不会触发此类的初始化(常量在链接阶段都已经存入调用类的常量池了)

 

 

 

 

 

 第二种情况:

 

 

 

 

 

 五.类加载器的作用

类加载器的作用:将class文件字节码内容加载到内存中,并且将这些静态数据转化为运行时的数据结构,然后再堆中生成一个代表这个类的java.lang.class对象,作为方法区中类的数据访问入口。

类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象

Java源文件(.java) ----》 Java编译器  ----》  字节码  (.class) -----》类加载器  ----》字节码校验器  ----》解释器  -----》操作系统平台

 

 

 

public class Test05 {
    public static void main(String[] args) throws ClassNotFoundException {
        //读取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
        
        //获取系统类的父类加载器---》扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@1540e19d
        
        //获取扩展器的父类加载器--》根加载器(c/c+++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);//null   根加载器是不允许直接被获取的
        
        //获取当前测试类是那个加载器
        Class<?> aClass = Class.forName("annotation.Test05");
        System.out.println(aClass);//class annotation.Test05
        
        //测试JDK内置类是谁加载的
        Class<?> aClass1 = Class.forName("java.lang.Object");
        System.out.println(aClass1);//class java.lang.Object
        
        //如何获取系统类加载器的路径
        System.out.println(System.getProperty("java.class.path"));
        /*
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;
        C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;
        D:\其它\Markdown\demo02\out\production\demo02;
        D:\其它1\Idea\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar

         */
    }
}

 

拓展(掌握):双亲委派机制

这个机制是用来维护Java运行的安全性的,当我们定义一个类之后,并不是直接加载这个类的,他会优先寻找它的父类,如果父类存在,就会继续找到根加载器,如果存在,就会加载这个类

反之,父类不存在,根加载器也不存在,才会加载自己的;这样就可以防止我们的普通代码类和系统代码类冲突

例子:比如我们定义了一个Java.lang.String  类,那么我们的编译器永远不会执行它,因为再根加载器也有一个相同的类

这个自定义类在启动加载时:

加载器的操作:

不会直接加载,先去寻找拓展类加载器,有没有Java.lang.String  很显然,它有这个类

继续寻找,在根加载器中寻找,很显然他也有这个类,所以这个Java.lang.String类在这个时候就被加载了,根本轮不到系统加载器去加你写的类

相反的操作:我们定义一个系统没有的 top.lostyou.test  到了根加载器,发现没有,

向下一级,查看扩展类加载器,结果是也没有

就会在系统加载器进行加载你写的类了

总而言之:除了系统加载器加载以外,其它的加载器都不加载我们自己写的类

六.获得类运行时的结构

  • Fieid                类的属性
  • Method            类的方法
  • Constructor     类的构造器
  • Superclass     此类的父类
  • Inteerface      类的接口
  • Annotation     类的注解
public class Test06 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> c1 = Class.forName("annotation.User");
        //获得类的名字   包名+ 类名        类名
        System.out.println(c1.getName());
        System.out.println(c1.getSimpleName());

        //获得类的属性
        Field[] fields = c1.getFields();//获得 public 属性
        for (Field field : fields) {
            System.out.println("public:"+field);
            /*
            public:public int annotation.User.classRoomNumber
             */
        }
        fields=c1.getDeclaredFields(); //所有的属性
        for (Field field : fields) {
            System.out.println("所有的属性:"+field);
            /*
            所有的属性:private int annotation.User.id
            所有的属性:private java.lang.String annotation.User.name
            所有的属性:private java.lang.String annotation.User.pwd
            所有的属性:public int annotation.User.classRoomNumber
             */
        }
        System.out.println("=====================================================");
        //获得类的方法
        Method[] methods = c1.getMethods();//获取自己和父类的所有public方法
        for (Method method : methods) {
            System.out.println("自己的方法,包括父类的public:"+method);
            /*
            自己的方法,包括父类的public:public java.lang.String annotation.User.toString()
            自己的方法,包括父类的public:public java.lang.String annotation.User.getName()
            自己的方法,包括父类的public:public int annotation.User.getId()
            自己的方法,包括父类的public:public void annotation.User.setName(java.lang.String)
            自己的方法,包括父类的public:public void annotation.User.setId(int)
            自己的方法,包括父类的public:public java.lang.String annotation.User.getPwd()
            自己的方法,包括父类的public:public void annotation.User.setPwd(java.lang.String)
            自己的方法,包括父类的public:public final void java.lang.Object.wait() throws java.lang.InterruptedException
            自己的方法,包括父类的public:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
            自己的方法,包括父类的public:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
            自己的方法,包括父类的public:public boolean java.lang.Object.equals(java.lang.Object)
            自己的方法,包括父类的public:public native int java.lang.Object.hashCode()
            自己的方法,包括父类的public:public final native java.lang.Class java.lang.Object.getClass()
            自己的方法,包括父类的public:public final native void java.lang.Object.notify()
            自己的方法,包括父类的public:public final native void java.lang.Object.notifyAll()
             */
        }
        methods=c1.getDeclaredMethods();//获取自己所有的方法
        for (Method method : methods) {
            System.out.println("自己的方法所有的方法"+method);
            /*
            自己的方法所有的方法public java.lang.String annotation.User.toString()
            自己的方法所有的方法public java.lang.String annotation.User.getName()
            自己的方法所有的方法public int annotation.User.getId()
            自己的方法所有的方法public void annotation.User.setName(java.lang.String)
            自己的方法所有的方法public void annotation.User.setId(int)
            自己的方法所有的方法public java.lang.String annotation.User.getPwd()
            自己的方法所有的方法public void annotation.User.setPwd(java.lang.String)
            自己的方法所有的方法private void annotation.User.getClassRoomNumber()
             */
        }
        //获得构造器
        Constructor<?>[] constructors = c1.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
            /*
            public annotation.User()
            public annotation.User(int,java.lang.String,java.lang.String)
             */
        }
        constructors=c1.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
            /*
            public annotation.User()
            public annotation.User(int,java.lang.String,java.lang.String)
             */
        }
    }
}

 

值得注意的是,不管是方法,属性,构造器,我们都可以获得指定的,但是由于重载的存在,我们获得指定的,必须要把类型丢进去

例如:Method getName = c1.getDeclaredMethod("setName",String.class);

七.动态创建对象和执行方法

思考:我们获得Class对象了,我们可以做些什么呢?
我们就可以用这个class对象创建类的对象:newInstance()方法

1.我们必须要有一个无参构造器

2.类的构造器访问权限足够

        Class c1 = Class.forName("annotation.User");
        //默认创建对象是无参构造实现的
        User user =(User) c1.newInstance();
        System.out.println(user);

 

有参构造也可以实现

1.通过class类的指定取构造器方法丢入参数结构类型

2.然后开始在此对象使用newInstance()中传入数据创建对象

3.通过Constructor

        //通过有参构造创建对象
        Constructor constructor = c1.getConstructor(int.class, String.class, String.class);  //
        User user1 = (User) constructor.newInstance(110, "msf", "mmm");
        System.out.println(user1);

 

其它操作

        //通过反射调用普通方法
        //invoke()  激活的意思
        //传参 : 对象   方法值
        Method setName = c1.getMethod("setName", String.class);
        setName.invoke(user,"i am gold");
        System.out.println(user.getName());

        //通过反射操作属性
        //私有属性不能直接操作,因为有反射有安全检测,且默认开启
        //name.setAccessible(true);  私有属性名 . setAccessible(true);  这样就完成关闭了
        Field name = c1.getDeclaredField("name");
        name.setAccessible(true);//关闭安全检测
        name.set(user,"gold");
        System.out.println(user.getName());

 

总结,我们在通过反射使用对象的方法,属性,有参构造等等,都要通过class对象先拿到,然后传入需要的类型然后固定是哪个方法

拓展:私有属性的操作

setAccessible()

对普通的属性可以直接操作,但是私有属性有安全检测,所以使用的时候必须要关闭安全检测

setAccessible(true/false)

true:关闭安全jianc

false:开启安全检测(默认开启的)

八.通过反射获取注解信息

ORM:Object  relationship  Mapping

对象关系映射

Java属性和数据库字段 一 一 对应

public class Test09 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("annotation.user");
        //通过反射获得类注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        
        //获得属性注解
        Field id = c1.getDeclaredField("id");
        word annotation = id.getAnnotation(word.class);
        System.out.println(annotation.columNmae());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}
 class user{
    @word(columNmae = "db_id",type = "int",length = 10)
    private int id;
    private int age;
    private String name;

     public user() {
     }

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

     public String getName() {
         return name;
     }

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

     @Override
     public String toString() {
         return "user{" +
                 "id=" + id +
                 ", age=" + age +
                 ", name='" + name + '\'' +
                 '}';
     }
 }
 @Target(ElementType.FIELD)
 @Retention(RetentionPolicy.RUNTIME)
 @interface word{
    String columNmae();
    String type();
    int length();
 }

 

标签:lang,反射,java,Reflection,public,加载,annotation,String
From: https://www.cnblogs.com/5ran2yl/p/16918933.html

相关文章

  • Hutool介绍(详)Java工具类库
    Hutool介绍(详)于 2022-05-0510:40:12 首次发布版权声明:本文为博主原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接和本声明。简介中文官网:https://plus.......
  • Java基础学习笔记(菜鸟教程版)
    Java基础学习笔记(菜鸟教程版)入门概览第一个Java程序helloworldpublicclassHello{publicstaticvoidmain(String[]args){/*pubic-->访问修饰符static-......
  • 桥接模式javac++
    [实验任务一]:两个维度的桥接模式用桥接模式实现在路上开车这个问题,其中,车可以是car或bus,路可以是水泥路或沥青路。     packagetest9;publicclassBusi......
  • 组合模式java+c++
    [实验任务一]:组合模式用透明组合模式实现教材中的“文件夹浏览”这个例子。    packagetest10;publicabstractclassAbstractFile{publicabstrac......
  • 装饰模式java+c++
    [实验任务一]:手机功能的升级用装饰模式模拟手机功能的升级过程:简单的手机(SimplePhone)在接收来电时,会发出声音提醒主人;而JarPhone除了声音还能振动;更高级的手机(ComplexP......
  • java集合对List进行排序
    List排序4种写法方式1:JAVA中我们可以使用java.util.Collections类的sort(Listlist)方法对list集合中的元素排序。方式2:JDK8之后特别是lambda表达式的盛行,而且Collec......
  • Java访问控制符
    Java访问控制符Java访问控制符修饰的对象可以分为两类:class和member(包括method和field)一.修饰classpublic:该类对所有类可见。default(不加修饰符):该类对同一个包下的......
  • Java 应用4核8G性能瓶颈分析步骤
     服务器性能瓶颈表现:CPU使用率达到了百分百什么原因会导致服务器CPU使用率达到百分百1、磁盘IO2、网络IO3、远程接口执行速度以上原因会导致服务响应变慢,为什么慢......
  • Java学习五
    一.小结1.程序模块化和可重用性是软件工程的中心目标之一。java提供了很多有助于完成这一目标的有效结构。方法就是一个这样的结构。2.方法指定方法的修饰符,返回值类型,方......
  • Java 设计模式:工厂模式
    目录工厂模式(FactoryPattern)实现简单工厂模式示例工厂方法模式示例抽象工厂模式示例参考工厂模式(FactoryPattern)所属:创建型模式,适用时机:我们明确地计划不同条件下创建......