反射最全内容,持续更新,通俗易懂,史上最强

本文详细介绍了Java反射的基本概念、使用场景,包括通过配置文件动态加载类、创建Class对象、获取和设置字段和方法,以及反射优化和常见练习示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

@[反射最全内容,持续更新,通俗易懂,史上最强]

反射的存在原因

当你要利用配置文件(Properties)给定的信息对相关类进行操作时,你会你怎么做?

假设给定的配置文件内容为:

classfullpath=com.hspedu.refleciton.Cat
method=cry

让你执行他的方法

我们的思路是不是首先要读取配置文件

Properties properties=new Properties();
properties.load(new FileInputStream("src/re.properties"));


//获取需要操作的类名
String classfullpath = properties.get("classfullpath").toString();

//获取需要操作的方法名字
String method = properties.get("method").toString();

但是这样你只是读取到了一个字符串,没法创建对象,总不能

new classfullpath() 把,这很明显是错误的。

所以在这个需求上,你就需要利用反射的知识进行解决。

反射基础知识
Java程序在计算机中有三个阶段

第一:编译阶段

即通过 javac编译 形成字节码文件

第二:加载阶段

通过类加载器(ClassLoader)体现反射,形成Class类对象,存放于方法区里,Class类对象里面包括 成员变量 Field[] fields、成员方法 Method [] ms、构造器 Contractor [] cons。

类的加载阶段又细分为三个过程:

①加载(Loading):他将.class文件读入内存,并位置创建一个java.lang.Class对象(类对象),这个过程由类加载器完成

②连接(Linking):连接由分为三个小部分

a:验证(verification):主要是安全校验

b:准备(preparation):对静态变量进行默认初始化,注意是默认初始化,一般基本数据类型赋值为0,引用数据类型赋值为null

c:解析(Resolution):Java虚拟机将常量池中的符号引用替换成直接引用。(思考一下,在类还未加载时,还没有地址,所以常量的地址是相对的,解析完成后,有了地址,常量的位置就是绝对的了),还有就是将.class文件的二进制数据合并到JRE中。

③初始化(Initialization):将静态代码块和静态变量写入内存

第三阶段:运行阶段

当我们new 了一个对象后,我们可以通过这个对象知道他是属于哪个Class对象,得到Class对象后,我们就可以利用这个Class对象操作这个类的方法、属性等等。

动态加载和静态加载的区别

这里验证不要用集成编译环境,因为太智能了,他会一下找到错误,想要切身体会,可以用记事本写下面的代码,用cmd运行一下:

静态加载:

import java.util.Scanner;
public class classload {
    public static void main(String[] args) {

        Scanner sc=new Scanner(System.in);
        int flag=sc.nextInt();
        switch (flag){
            case 1:
                Person p=new Person();
                break;
            case 2:
                System.out.println("尚硅谷");
                break;
        }
    }
}

上述代码你在用cmd运行时会发现,他连编译都通过不了,因为我们没有Person这个实体类,也就是说,在编译时,他就会加载相关的类,如果没有就会报错,依赖性太强。

动态加载:

import java.util.Scanner;
public class classload {
    public static void main(String[] args) throws Exception{

        Scanner sc=new Scanner(System.in);
        int flag=sc.nextInt();
        switch (flag){
            case 1:
                Class<?> clazz = Class.forName("com.hspedu.reflection.Person");
                Object object = clazz.newInstance();
                break;
            case 2:
                System.out.println("韩顺平");
                break;
        }
    }
}

上述代码在用cmd运行时,你会发现他可以通过编译,虽然这个java文件里也没有写Person类,只有选1时,进行加载才会报错,这就是反射的动态加载机制。

得到Class对象的六种方式
//1.Class.forName
Class clazz1=Class.forName("com.hspedu.Car");//()中的内容一般通过读取配置文件内容获取
System.out.println(clazz1);

//2.类名.class 应用场景:参数传递
Class clazz2=Car.class;
System.out.println(clazz2);//前面利用构造器中传参

//3.对象.getClass(),应用场景,有对象实例
Car car = new Car();
Class<? extends Car> clazz3 = car.getClass();
System.out.println(clazz3);

//4.通过类的加载器来获取Class对象
//(1)先得到类加载器
ClassLoader classLoader = car.getClass().getClassLoader();
//(2)通过类加载器得到Class对象
Class<?> clazz4 = classLoader.loadClass("com.hspedu.Car");
System.out.println(clazz4);

上述四种为最重要的

//5.基本数据类型(int,double......)
Class<Integer> integerClass = int.class;
Class<Double> doubleClass = double.class;

//6.包装类(Integer、Double.....)
Class<Integer> type = Integer.TYPE;
Class<Double> type1 = Double.TYPE;
//这里给大家抛出一个问题 
System.out.println(tpye==integerclass)//true or false
反射机制创建实例

Class.forName(),()中写你准备加载的类的相对路径。

比如说你在com.hspedu.refleciton包下写了Cat,则()中写com.hspedu.refleciton.Cat

Class clazz=Class.forName(com.hspedu.refleciton.Cat);

clazz就是我们获取的Class类型的对象,简称类对象

Object obj=clazz.newInstance();

clazz通过newInstance()方法获取类com.hspedu.refleciton.Cat的对象实例,需要注意的是,这里我们只能利用空参构造器进行初始化。

  • 构造器有关内容

当我们通过public有参构造获取类对象实例时

public User(String name){
    this.name=name;
}
Class<?> clazz = Class.forName("com.hspedu.refleciton.classload.User");
Constructor<?> constructor = clazz.getConstructor(String.class);
Object object1 = constructor.newInstance("尚硅谷");

当我们通过非public有参构造获取类对象实例时

private User(int age, String name) {
    this.age = age;
    this.name = name;
}
Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(int.class, String.class);//getdeclaredConstructor得到类中所有构造器实例
declaredConstructor.setAccessible(true);//暴力破解,使反射可以访问private的构造器
User object2 = (User) declaredConstructor.newInstance(24, "lsp");

构造器实例有两个常用方法:

1.getModifiers:
以int形式返回修饰符
默认修饰符:0  public:1   private:2     protected:4
static  :8  final: 16
如果是 public static 则为 9
2.getReturnType()是获得返回值类型对应的Class对象
  • 属性(字段)有关内容
Class<?> clazz = Class.forName("com.hspedu.refleciton.classload.Student");
Object object = clazz.newInstance();//o 的运行类型就是Student
System.out.println(object.getClass());//Student
Field age = clazz.getField("age");
age.set(object,12);//修改字段内容
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);//这里name是私有的且被static修饰
name.set(null,"李白");//因为name是static修饰的,所以可以用null也可以用object
System.out.println(object);
System.out.println(name.get(object));//获取字段值
  • 方法有关内容

    clazz通过getMethod()方法获取类com.hspedu.refleciton.Cat的方法实例(这里体现了Java的万事万物皆对象),我们把Cat中的方法,看成一个方法对象。

    然后我们通过 方法对象 去调用方法

    method1.invoke(obj);//这里就体现出反射和传统方法的区别了

    如果method1方法对象所对应的方法有参数,则写成

    method1,invoke(obj,实参,实参…)

    传统方法是:对象名.方法();

    反射机制是:方法对象.invoke(对象名,实参,实参…);

Class<?> clazz = Class.forName("com.hspedu.refleciton.classload.Boss");
Object object = clazz.newInstance();
Method hi = clazz.getMethod("hi", String.class);//获取方法名为hi,参数类型为String的方法
hi.invoke(object, "啥大椿");//给object类中的hi方法的参数赋值为 啥大椿

Method say = clazz.getDeclaredMethod("say", int.class, String.class, char.class);
say.setAccessible(true);
//对于反射来讲,如果方法有返回值,统一返回Objec,但是其运行是类型为say方法的返回值类型
Object invoke = say.invoke(null, 11, "夏洛", '男');//null可以替换成 Object
System.out.println(invoke);
System.out.println(invoke.getClass());//String

//方法实例有两个特有的方法:
// 1.getReturnType()是获得返回值类型对应的Class对象
   
// 2.getParameterTypes()是获得参数数组,每一个参数的类型对应的Class对象
反射常用的Utils
Class<?> clazz1 = Class.forName("com.hspedu.refleciton.classload.Person");
//getName()  :获取全类名
String name = clazz1.getName();
//getSimpleName  :获取简单类名
String simpleName = clazz1.getSimpleName();
//获取所有public 修饰的属性,包含本类以及父类
Field[] fields = clazz1.getFields();
//获取本类的所有属性
Field[] declaredFields = clazz1.getDeclaredFields();
//获取本来及父类的所有方法---->父类包括 父类的父类
Method[] methods = clazz1.getMethods();
//获取本类中的所有方法
Method[] declaredMethods = clazz1.getDeclaredMethods();
//获取包含public修饰的构造器,不包括父类的
Constructor<?>[] constructors = clazz1.getConstructors();
//获取本类所有的构造器
Constructor<?>[] declaredConstructors = clazz1.getDeclaredConstructors();
//返回包信息
Package aPackage = clazz1.getPackage();
//返回父类的全路径
Class<?> superclass = clazz1.getSuperclass();
//返回接口信息
Class<?>[] interfaces = superclass.getInterfaces();
//返回注解信息
Annotation[] annotations = clazz1.getAnnotations();
反射优化

通过反射调用方法,比传统方法耗时多的多,可以关掉反射检查来减少一部分时间

public class Reflection02 {
    public static void main(String[] args) throws Exception {
        m1();//4
        m2();//1610
        m3();//772

    }

    //传统方法类调用方法所用时间
    public static void m1(){
        Cat cat=new Cat();
        long start=System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            cat.hi();
        }
        long end=System.currentTimeMillis();
        System.out.println(end-start);

    }
    //利用反射机制调用方法所用时间
    public static void m2() throws Exception {
        Class clazz=Class.forName("com.hspedu.refleciton.Cat");
        Object object = clazz.newInstance();
        Method hi = clazz.getMethod("hi");

        long start=System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            hi.invoke(object);
        }
        long end=System.currentTimeMillis();
        System.out.println(end-start);

    }
    //反射优化后所用时间
    public static void m3() throws Exception {
        Class clazz=Class.forName("com.hspedu.refleciton.Cat");
        Object object = clazz.newInstance();
        Method hi = clazz.getMethod("hi");
        hi.setAccessible(true);//在反射调用是,关闭访问检查!
        long start=System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            hi.invoke(object);
        }
        long end=System.currentTimeMillis();
        System.out.println(end-start);
    }
}
练习1:

通过反射修改私有成员变量 com.hspedu.homework Homework01.java

1.定义PrivateTest类,有私有name属性并且属性值为hellokitty
2.提供getName的公有方法
3.创建PrivateTest的类,利用Class类得到私有的name属性,修改私有的name属性值,并调用getName()的方法打印name属性值

class PrivateTest{
    private String name="helloKitty";
    public String getName(){
        return name;
    }
}
练习2:

利用反射和File完成以下功能 Homework02.java

1.利用Class类的forName方法得到File类的class 对象
2.在控制台打印File类的所有构造器
3.通过newInstance的方法创建File对象,并创建E:\mynew.txt文件
提示:创建文件的正常写法如下:

File file = new File(“d:\aa.txt”);

file.createNewFile();

练习1代码:
这里只是其中的一种方式:

import java.lang.reflect.Field;

public class Homework01 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.hspedu.refleciton.homework.PrivateTest");
        Object object = clazz.newInstance();
        Field name = clazz.getDeclaredField("name");
        name.setAccessible(true);
        name.set(object,"GoSiLin");
        Object object1 = name.get(object);
        System.out.println(object1);


    }
}

class PrivateTest{
    private String name="helloKitty";
    public String getName(){
        return name;
    }
}

练习2代码:

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

public class Homework02 {
    public static void main(String[] args) throws Exception{


        Class<?> fileClazz = Class.forName("java.io.File");
        Constructor<?>[] declaredConstructors = fileClazz.getDeclaredConstructors();
        for(Constructor<?> d:declaredConstructors){
            System.out.println(d);
        }

        Constructor<?> declaredConstructor = fileClazz.getDeclaredConstructor(String.class);
        String filePath="f:\\aa.txt";
        Object file= declaredConstructor.newInstance(filePath);
        Method createNewFile = fileClazz.getMethod("createNewFile");
        createNewFile.invoke(file);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值