Java教程

Java反射

本文主要是介绍Java反射,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

1 反射基本用法

1.1 常用接口

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

    Properties properties=new Properties();
    properties.load(new FileInputStream("src/main/resources/res.properties"));
    String classfullpath = properties.getProperty("classfullpath").toString();
    String methodName = properties.getProperty("method").toString();
    System.out.println(classfullpath);
    System.out.println(methodName);
    //1、加载类
    Class cls=Class.forName(classfullpath);
    //2、得到类的对象实例
    Object o = cls.newInstance();
    //3、获取方法对象
    Method method = cls.getMethod(methodName);
    //4、方法执行
    method.invoke(o);

    //获取所有public字段
    Field[] fields = cls.getFields();
    for (Field field:fields){
        System.out.println("字段名称:"+field.getName());
        System.out.println("字段值:"+field.get(o));
    }
    //获取所有public方法
    Method[] methods = cls.getMethods();
    for (Method method1:methods){
        System.out.println("方法名称:"+method1.getName());
    }
    //构造器
    Constructor constructor = cls.getConstructor();
    System.out.println(constructor);
    
}

1.2 、反射优化

在这里插入图片描述

2 获取类结构信息

2.1、Class类

  1. Class也是类,因此也继承Object类

  2. Class类对象不是new出来的,而是系统创建的

  3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次

  4. 每个类的实例都会记得自己是由哪个Class 实例所生成

  5. 通过Class可以完整地得到一个类的完整结构,通过一系列API

  6. Class对象是存放在堆的

  7. 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码
    变量名,方法名,访问权限等等)https://www.zhihu.com/question/384969073

    2.1.1 Class常用方法

方法名称功能说明
static Class forName(String name)返回指定类名name的Class 对象
Object newlnstance()调用缺省构造函数,返回该Class对象的一个实例
getName()返回此Class对象所表示的实体(类、接口、数组类、基本类型等)名称
Class getSuperClass()返回当前Class对象的父类的Class对象
Class []getlnterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的类加载器
Constructor[] getConstructors()返回一个包含某些Constructor对象的数组
Field[] getDeclaredFields()返回Field对象的一个数组(私有和公有字段)
Field[] getFields()返回Field对象的一个数组(公有字段)
Method getMethod返回一个Method对象,此对象的形参类型为paramType
Method getMethod(String name,Class … paramTypes)返回一个Method对象,此对象的形参类型为paramType

2.1.2 获取Class类对象的方式

  1. 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,实例:

    Class cls1 =Class.forName( "java.lang.Cat”)
    
  2. 前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高实例: Class cls2 = Cat.class;
    应用场景:多用于参数传递,比如通过反射得到对应构造器对象.

  3. 前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:Class clazz =对象.getClass();
    应用场景:通过创建好的对象,获取Class对象.

  4. 其他方式

  ClassLoader cl =对象.getClass(0).getClassLoaderO);
  
  Class clazz4 = cl.loadClass(“类的全类名”);
  1. 基本数据(int, char,boolean,float,double,byte,long, short)按如下方式得到Class类

    Class cls =基本数据类型.class
    
  2. 基本数据类型对应的包装类,可以通过.type得到Class类对象

    Class cls =包装类.TYPE  
    
    Class<Integer> type = Integer.TYPE;
    

2.1.3 哪些类型有Class对象

1.外部类,成员内部类,静态内部类,局部内部类,匿名内部类

  1. interface:接口

  2. 数组

  3. enum:枚举

  4. annotation:注解

  5. 基本数据类型

  6. void

2.2 Field类

java.lang.reflect.Field 为我们提供了获取当前对象的成员变量的类型,和重新设值的方法。

获取变量的类型

类中的变量分为两种类型:基本类型引用类型

  • 基本类型( 8 种)
    • 整数:byte, short, int, long
    • 浮点数:float, double
    • 字符:char
    • 布尔值:boolean
  • 引用类型
    • 所有的引用类型都继承自 java.lang.Object
    • 类,枚举,数组,接口都是引用类型
    • java.io.Serializable 接口,基本类型的包装类(比如 java.lang.Double)也是引用类型

java.lang.reflect.Field 提供了两个方法获去变量的类型:

  • Field.getType():返回这个变量的类型
  • Field.getGenericType():如果当前属性有签名属性类型就返回,否则就返回 Field.getType()
Class<?> getType() //返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

Type getGenericType() //返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。

2.3 Method类

2.4 Constructor类

3 类加载机制

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。

1.***静态加载:***编译时加载相关的类,如果没有则报错,依赖性太强

  1. 动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在也不报错,降低了依赖性

3.1 类加载时机

1.当创建对象时(new) (静态加载)

2.当子类被加载时,父类也被加载 (静态加载)

3.调用类中的静态成员时(静态加载)

4.通过反射(动态加载)

Class.forName("com.test.Cat");

3.2 类加载过程

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

3.2.1 加载阶段

JVM在该阶段的主要目的是将字节码从不同的数据源(可能是 class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class 对象

3.2.2 链接阶段——验证

1.目的是为了确保Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

2.包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证

3.可以考虑使用**-Xverify:none** 参数来关闭大部分的类验证措施,缩短虚拟机类加载

3.2.3 链接阶段——准备

JVM 会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0、0L、null、false 等)。这些变量所使用的内存都将在方法区中进行分配。

class A{

    //1、n1是实例属性,不是静态变量,因为在准备阶段是不会分配内存的
    //2、n2是静态变量,在链接初始化阶段,分配内存n2默认初始化是0
    //3、n3是final static常量(一旦赋值不会改变),在链接初始化阶段,分配内存n3默认初始化是30
    public int n1=10;
    public static int n2=20;
    public final static int n3=30;

}

3.2.4 链接阶段——解析

虚拟机将常量池内的符号引用替换为直接引用(内存地址)的过程

3.2.5 初始化阶段

1.到初始化阶段,才真正开始执行类中定义的 Java程序代码,此阶段是执行()方法的过程。

  1. ()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
  2. 虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕

4、通过反射访问类中的成员

4.1、通过反射创建对象的方式

  1. 调用类中public修饰的无参构造函数
  2. 调用类中指定的构造器
package org.example.reflection;

import java.lang.reflect.Constructor;

/**
 * @author joy
 * @create 2021-09-16 10:02
 * @description
 */
public class ReflectionObjectDemo {

    public static void main(String[] args) throws Exception {
          //1、获取到User类的Class对象
          Class classUser=Class.forName("org.example.reflection.User");
          //2、通过public的无参构造函数获取对象
        Object o = classUser.newInstance();
        System.out.println(o);
        //3、通过public的有参构造函数获取对象
        Constructor constructorUser = classUser.getConstructor(String.class);
        Object user1 = constructorUser.newInstance("yyc");
        System.out.println(user1);
        //4、通过非public的有参构造函数获取对象
        Constructor declaredConstructor = classUser.getDeclaredConstructor(int.class, String.class);
        //值true表示反射对象在使用时应禁止Java语言访问检查。值false表示反射对象应强制执行Java语言访问检查
        //爆破,访问私有属性、构造器、方法等
        declaredConstructor.setAccessible(true);
        Object user2 = declaredConstructor.newInstance(12, "yyc");
        System.out.println(user2);
    }

}

class User{

    public int age=1;
    public String name="xiaoma";

    public User(){

    }

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

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

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

4.2、通过反射访问属性

package org.example.reflection;

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

/**
 * @author joy
 * @create 2021-09-16 10:02
 * @description
 */
public class ReflectionObjectDemo {

    public static void main(String[] args) throws Exception {
        //1、获取到User类的Class对象
        Class classUser = Class.forName("org.example.reflection.User");
        //获取对象
        Object o = classUser.newInstance();
        //获取字段
        Field name = classUser.getField("name");
        //设置字段值
        name.set(o, "xiaozhang");
        //获取属性值
        Object nameObj = name.get(o);
        System.out.println(nameObj);
        System.out.println(o);

        Field address = classUser.getDeclaredField("address");
        address.setAccessible(true);
        address.set(o,"usa");
    }

}

class User {

    public int age = 1;
    public String name = "yyc";
    private String address="china";
    public User() {

    }

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

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

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

4.3、通过发射操作方法

1.根据方法名和参数列表获取Method方法对象: Method m =clazz.getDeclaredMethod(方法名,XX.class);//得到本类的所有方法·

2.获取对象:Object o=clazz.newlnstance();

  1. 暴破:m.setAccessible(true);
  2. 访问:Object returnValue = m.invoke(o.实参列表):
  3. 注意:如果是静态方法,则invoke的参数o,可以写成null
package org.example.reflection;

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

/**
 * @author joy
 * @create 2021-09-16 10:02
 * @description
 */
public class ReflectionObjectDemo {

    public static void main(String[] args) throws Exception {
        //1、获取到User类的Class对象
        Class classUser = Class.forName("org.example.reflection.User");
        //获取对象
        Object o = classUser.newInstance();

        Method getAddress = classUser.getMethod("getAddress");
        Object invokeResult = getAddress.invoke(o);
        System.out.println(invokeResult);
    }

}

class User {

    public int age = 1;
    public String name = "yyc";
    private String address="china";
    public User() {

    }

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

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

    public String getAddress(){
        return  address;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
这篇关于Java反射的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!