Java反射机制

狂神说笔记

静态语言、动态语言

动态语言

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

静态语言

  • 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如:Java、C、C++。
  • Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

Java Reflection

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

  • Class c= Class.forName(“java.lang.String”)

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

正常方式:引入需要的"包类"名称->通过new实例化->取得实例化对象。
反射方式:实例化对象->getClass()方法->得到完整的“包类”名称。

Java反射机制提供的功能

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

代码举例:

package com.tz.reflection;


//什么叫做反射
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("com.tz.reflection.User");
        System.out.println(c1);

        Class c2 = Class.forName("com.tz.reflection.User");
        Class c3 = Class.forName("com.tz.reflection.User");

        //一个类在内存中只有一个class对象
        //一个类被加载后,类的整个结构都会被封装在class对象中
        System.out.println(c1.hashCode());  //460141958
        System.out.println(c2.hashCode()); //460141958
        System.out.println(c3.hashCode()); //460141958

    }
}
class User{
    private String name;
    private int id;
    private int age;

    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;
    }

    public User() {
    }

    public User(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

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

总结:

  • 一个类在内存中只有一个class对象。
  • 一个类被加载后,类的整个结构都会被封装在class对象中。

Class类

-对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口.对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/)的有关信息。

  • Class本身也是一个
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class 实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

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

代码举例:

package com.tz.reflection;

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException {
        //方式一:通过对象获得
        Person person=new Person();
        Class c1 = person.getClass();
        System.out.println(c1);
        //方式二:forname获得
        Class c2 = Class.forName("com.tz.reflection.Student");
        System.out.println(c2.hashCode());
        //方式三:通过类名.class获得
        Class<Student> c3 = Student.class;
        System.out.println(c3.hashCode());
        //方式四:基本内置类型的包装类都有一个Type类型
        Class<Integer> c4 = Integer.TYPE;
        System.out.println(c4.getClasses());
        //获得父类类型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}
class Person{
    public String name;

    public Person(String name) {
        this.name = name;
    }

    public Person() {
    }

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

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

class Teacher extends Person{
    public Teacher() {
        this.name="老师";
    }
}

在这里插入图片描述

package com.tz.reflection;

import java.lang.annotation.ElementType;

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

       //只要元素类型与维度一样,就是同一个class
        int [] a=new int[10];
        int [] b=new int[100];
        System.out.println(a.getClass().hashCode());  //460141958
        System.out.println(b.getClass().hashCode());  //460141958

    }
}

类加载内存分析

在这里插入图片描述

类的加载过程

在这里插入图片描述

类的加载与ClassLoader的理解

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

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

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

3.初始化:

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

代码举例:

package com.tz.reflection;

public class Test4 {
    public static void main(String[] args) {
        /*
        1.记载到内存,会产生一个类对应class对象
        2. 链接,链接结束后 m=0
        3.初始化
            <clinit>(){
                System.out.println("A类静态代码块初始化");
                m=300;
                m=100;
            }
         */
        A a=new A();
        System.out.println(A.m);//结果:A类静态代码块初始化,A类的无参构造初始化,100

    }
}
class A{
    static {
        System.out.println("A类静态代码块初始化");
        m=300;
    }

    static int m =100;

    public A() {
        System.out.println("A类的无参构造初始化");
    }
}

结果:

A类静态代码块初始化
A类的无参构造初始化
100

什么时候会发生初始化?

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

  • 当虚拟机启动,先初始化main方法所在的类
  • new一个类的对象
  • 调用类的静态成员(除了final常量)和静态方法
  • 使用java.lang.reflect包的方法对类进行反射调用
    当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类类的

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

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
  • 通过数组定义类引用,不会触发此类的初始化
  • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

类加载器

在这里插入图片描述
在这里插入图片描述
代码举例:

package com.tz.reflection;

public class Test5 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //获取系统加载器的父类加载器-->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        //获取扩展类加载器的父类加载器-->根加载器(c/c++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);//结果为null,此加载器不能直接获取!
        //测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("com.tz.reflection.Test5").getClassLoader();

        //如何获得系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        /*
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\charsets.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\deploy.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\access-bridge-64.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\cldrdata.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\dnsns.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\jaccess.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\jfxrt.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\localedata.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\nashorn.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\sunec.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\sunjce_provider.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\sunmscapi.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\sunpkcs11.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext\zipfs.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\javaws.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\jce.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\jfr.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\jfxswt.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\jsse.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\management-agent.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\plugin.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\resources.jar;
        C:\Program Files\Java\jdk1.8.0_192\jre\lib\rt.jar;
        C:\Users\86199\IdeaProjects\my07\target\classes;
        C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar

         */


    }
}

什么是双亲委派机制

当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。否则加载上级的这个类。

获取类的运行时结构

在这里插入图片描述
代码举例:

package com.tz.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test6 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1=Class.forName("com.tz.reflection.User");

        //获得类的名字
        System.out.println(c1.getName()); //获得包名+类名
        System.out.println(c1.getSimpleName());//获得类名

        //获得类的属性
        System.out.println("===========================");
        Field[] fields = c1.getFields(); //只能获得public的属性
        fields=c1.getDeclaredFields();//找到全部的属性

        for (Field field:fields
             ) {
            System.out.println(field);
        }
        //获得指定的属性
        System.out.println("===========================");
        Field name = c1.getDeclaredField("name");
        System.out.println(name);
        //获得类的方法
        System.out.println("===========================");
        Method[] methods = c1.getMethods(); //获得本类及其父类的全部public方法
        for(Method method:methods){
            System.out.println(method);
        }
        System.out.println("===========================");
        methods=c1.getDeclaredMethods();
        for (Method method:methods
             ) {
            System.out.println(method);
        }

        //获得指定方法
        System.out.println("===========================");
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        //获得构造器
        System.out.println("===========================");
        Constructor[] constructors = c1.getConstructors();//public
        for (Constructor constructor:constructors
             ) {
            System.out.println(constructor);
        }
        constructors=c1.getDeclaredConstructors();
        for (Constructor constructor:constructors
        ) {
            System.out.println(constructor);
        }
        //获得指定构造器
        System.out.println("===========================");
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println(declaredConstructor);
    }
}

结果:

com.tz.reflection.User
User
===========================
private java.lang.String com.tz.reflection.User.name
private int com.tz.reflection.User.id
private int com.tz.reflection.User.age
===========================
private java.lang.String com.tz.reflection.User.name
===========================
public java.lang.String com.tz.reflection.User.toString()
public java.lang.String com.tz.reflection.User.getName()
public int com.tz.reflection.User.getId()
public void com.tz.reflection.User.setName(java.lang.String)
public int com.tz.reflection.User.getAge()
public void com.tz.reflection.User.setAge(int)
public void com.tz.reflection.User.setId(int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
===========================
public java.lang.String com.tz.reflection.User.toString()
public java.lang.String com.tz.reflection.User.getName()
public int com.tz.reflection.User.getId()
public void com.tz.reflection.User.setName(java.lang.String)
public int com.tz.reflection.User.getAge()
public void com.tz.reflection.User.setAge(int)
public void com.tz.reflection.User.setId(int)
===========================
public java.lang.String com.tz.reflection.User.getName()
public void com.tz.reflection.User.setName(java.lang.String)
===========================
public com.tz.reflection.User()
public com.tz.reflection.User(java.lang.String,int,int)
public com.tz.reflection.User()
public com.tz.reflection.User(java.lang.String,int,int)
===========================
public com.tz.reflection.User(java.lang.String,int,int)

动态创建对象执行方法

在这里插入图片描述
代码举例:

package com.tz.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//动态的创建对象,通过反射
public class Test7 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得class对象
        Class<?> c1 = Class.forName("com.tz.reflection.User");
        //构造一个对象
        User user = (User) c1.newInstance();//本质是调用了类的无参构造器
        System.out.println(user);

        //通过构造器创建对象
        Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 = (User)constructor.newInstance("张三", 001, 18);
        System.out.println(user2);
        //通过反射调用普通方法
        User user3 = (User) c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke:激活的意思
        setName.invoke(user3,"李四");
        System.out.println(user3.getName());

        //通过反射操作属性
        System.out.println("==============================");
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");

        //不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
        name.setAccessible(true);
        name.set(user4,"钱七");
        System.out.println(user4.getName());


    }
}

结果:

User{name='null', id=0, age=0}
User{name='张三', id=1, age=18}
李四
==============================
钱七

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

获取泛型信息

在这里插入图片描述
在这里插入图片描述
代码举例:

package com.tz.reflection;

import java.lang.annotation.*;
import java.lang.reflect.Field;

public class Test8 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class<?> c1 = Class.forName("com.tz.reflection.Student2");

        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //获得注解的value值
        Tablekuang tablekuang = c1.getAnnotation(Tablekuang.class);
        String value = tablekuang.value();
        System.out.println(value);

        //获得类指定的注解
        Field f = c1.getDeclaredField("id");
        Fieldkuang annotation = f.getAnnotation(Fieldkuang.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}
@Tablekuang("db_student")
class Student2{
    @Fieldkuang(columnName = "db_id",type = "int",length =10)
    private int id;
    @Fieldkuang(columnName = "db_age",type = "int",length =10)
    private int age;
    @Fieldkuang(columnName = "db_id",type = "varchar",length =3)
    private String name;

    public Student2(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public Student2() {
    }

    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 "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablekuang{
    String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldkuang{
    String columnName();
    String type();
    int length();
}

结果:

@com.tz.reflection.Tablekuang(value=db_student)
db_student
db_id
int
10

总结:

通过反射我们可以获取一个类的信息并进行一些其他操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值