Java基础巩固-了解Java中的反射机制

简介: 最近有空的时候会看下jdk和spring的源码,发现反射的使用是非常频繁的。之前也对反射或多或少有过了解,但也只是停留在了解的阶段,总结一下来加深自己的印象。

最近有空的时候会看下jdk和spring的源码,发现反射的使用是非常频繁的。之前也对反射或多或少有过了解,但也只是停留在了解的阶段,总结一下来加深自己的印象。

反射的基本概念:程序可以访问、检测和修改其本身状态或行为的一种能力。

反射机制是java的特性之一,指的是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。(摘自百度)

反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取它所有的成员变量和方法并且显示出来(官方概念)

常见应用场景

1.各种框架中,比如spring的IOC(控制反转)

spring ioc的思想是将设计好的对象交给容器控制,帮我们实例化,其中就有用到反射来实现。

大致步骤(伪代码)
①spring配置好bean
<bean id="courseDao" class="com.qcjy.learning.Dao.impl.CourseDaoImpl"></bean>  
②解析bean中class属性
③通过反射获取Class对象
Class<?> cls = Class.forName(classStr);  
④实例化对象
Object obj = cls.newInstance();
⑤放到容器中

2.tomcat读取web.xml

tomcat服务器提供了处理请求和应答的方式,针对不同处理动作,对外提供接口让开发者做相应的具体实现。

参考这里

学习反射机制前,需要先简单了解一下JVM,java之所以能跨平台,是因为java虚拟机。类的加载和运行都是依托它。

img_b601448d33cc04e7938ba6f9f159c276.jpe
jvm内存区域
举个栗子:
Object object = new Object();
运行该程序顺序

1.JVM启动,先将代码编译成.class文件,根据类加载器(Object类由顶层父类Boostrap ClassLoader)加载这个class文件并加载到jvm内存中
2.方法区存类的信息,类加载器通过方法区上类的信息在堆上创建类的Class对象(不是new出来的对象,而是类的类型对象,每个类只有一个Class对象,该Class对象由jvm保证唯一,之后类的创建根据这个Class对象操作)。
3.jvm创建对象前,会先检查类是否被加载,如果加载好,则为对象分配内存,初始化就是代码:new Object()

这种new出对象的方式在实际应用中很常见,相当于程序相当于写死了给jvm去跑。假如一个服务器上突然遇到某个请求要用到某个类,但没加载进jvm,这种时候总不能停下来再new一个这个类的对象,之后再重启服务器。。

这个时候回到java反射的概念。简单来说能动态获取一个类的信息并且去操作它(属性,方法等等…)。它允许在程序在运行时加载,探知使用编译期间已知或未知的class,method,属性,参数等等。

动态加载和静态加载

动态加载:程序在运行期间调用方法,即使方法是错误的程序依旧执行,通过动态加载可以使程序更加灵活方便日后维护

静态加载:程序在编译时执行,在此过程中只要方法出错,编译器会报错,就会中断程序,这是我们常用的。

而反射恰好在编译时不执行,而是在运行期间生效。

jdk中反射中常用的类

java.lang.Class
java.lang.reflect.Constructor
java.lang.reflect.Field        
java.lang.reflect.Method
java.lang.reflect.Modifier

反射可以做的事

package reflect;

//Person类用于后续测试
public class Person {
    private String sex;
    public String age;
    public String work;
    public String name;
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public String getWork() {
        return work;
    }
    public void setWork(String work) {
        this.work = work;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Person() {
        System.out.println("公有,调用无参的构造方法");
    }
    public Person(String name, String sex) {
        System.out.println("公有,调用有参构造方法" + "name=" + name + "sex" + sex);
    }
    private Person(String sex) {
        System.out.println("调用私有,sex=" + sex);
    }
    public void sayHello(String word) {
        System.out.println("公有方法说的话->" + word);
    }
    private void say(String word) {
        System.out.println("私有方法说的话->" + word);
    }
}

1.获取Class对象(使用Person类做演示)

/**
 * 获取Class对象的三种方式
 */
public class ClassTest {
    public static void main(String[] args) throws ClassNotFoundException {
        /**
         * 1. 通过getClass
         *  Object类和Class类都有getClass()本地方法用于获取
         *  在Java中所有的类都继承于Object类,但不用在声明一个类时显示的extends Object
         *  这边采用的是Object类里的getClass()方法
         */
        Object o = "";
        System.out.println("o->" + o.getClass()); //o->class java.lang.String
        Person p = new Person();
        Class clazz1 = p.getClass();
        //两个Person对象不同 但类是相同的 就好比说两个人有各个方面不同 但都是人这个类
        Person p1 = new Person();
        System.out.println("p->" + clazz1);//class reflect.Persons
        System.out.println(p == p1); //false
        System.out.println(p.getClass() == Person.class); //true
        System.out.println(p.getClass() == p1.getClass()); //true
        /**
         * 2.类名.class
         */
        Class clazz2 = Person.class;//class reflect.Person

        /**
         * 3.Class.forName() jdbc也是通过这种方式加载驱动类
         */
        Class clazz3 = Class.forName("reflect.Person");

        /**
         * 总结:
         *  三种获取Class对象方法 1.Object类的getClass() 2.类名.class 3.Class.forName()
         *  判断实例对象类型 getClass与instance的区别在于getClass不考虑继承 intance的话如果是父类也属于
         *  在运行期间,一个类只有一个Class对象产生 Class是单例的 验证如下
         *  相比之下 1已经有对象了还要反射显得没啥意义 2要导入类 依赖强 3第三种实用性更强
         */
        //true意味着三个类对象是同一个类(Class)的实例
        System.out.println("class对象是否相等->" + (clazz1 == clazz2 && clazz1 == clazz3));//class对象是否相等->true
        System.out.println(p.getClass() == o.getClass());//false
        System.out.println(p instanceof Person && p instanceof Object); //true
    }
}

2.创建对象

这边提一下java创建对象的5种方式

1.使用new
2.使用java.lang.Class类的newInstance()
3.使用Constructor类的newInstance方法
4.使用clone方法 写法类似
Person person = Person.class.newInstance();
Person personClone = (Person)person.clone();
这边被克隆的类要实现Cloneable并重写clone方法 虽然Cloneable接口为空但也要实现作为标志,否则object的clone方法将报CloneNotSupportedException的错
5.使用反序列化 序列化后对象反序列化 
6.动态代理Proxy方式 Proxy的静态方法newProxyInstance

这边2,3,6是使用反射的机制来创建对象。

样例代码

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    /**
     * 利用反射机制中的newInstance方法创建对象
     */
    Class clazz = Class.forName("reflect.Person"); //先获取要创建对象的类的Class对象
    Object o = clazz.newInstance();
    System.out.println(o.getClass());//class reflect.Person
    /**
     * 通过Constructor类创建
     * 通过getConstructors方法获得构造器Constructor对象并创建
     */
    Object o1 = clazz.getConstructor().newInstance();
    System.out.println(o1.getClass());//class reflect.Person

    //可通过创建string对象
    Object o2 = String.class.getConstructor(String.class).newInstance("hello world");//class reflect.Person
    System.out.println(o2);//hello world
}

3.获取成员变量并调用

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Class personClass = Class.forName("reflect.Person");
        //获取共有字段 如果是private修饰会报NoSuchFieldException
        Field f = personClass.getField("name");
        System.out.println(f); //public java.lang.String reflect.Person.name

        Field[] af = personClass.getFields();
        System.out.println(af);//对象数组 可通过遍历获取
        System.out.println(Arrays.stream(af).count()); //公有数目为3

        //公有私有都能获取
        Field f1 = personClass.getDeclaredField("sex");//private java.lang.String reflect.Person.sex
        System.out.println(f1);
        //获取公有字段并调用
        Object object = personClass.getConstructor().newInstance();
        f.set(object, "garwer");
        Person person = (Person)object;
        System.out.println((person.name));//garwer

        //获取私有字段并调用 强制变性 - -
        Person nan = (Person) personClass.newInstance();
        nan.setSex("女");
        f1.setAccessible(true);//暴力反射 解除私有限定 如果不加上这句会报错
        f1.set(nan,"男");
        System.out.println(nan.getSex()); //输出男
    }

4.获取方法(包含构造方法和成员方法)并调用

package reflect;

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

public class TestMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //获取Class对象
        Class personClass = Class.forName("reflect.Person");
        /**
         * (一)构造方法 
           名字定义与类名相同 无返回类型业务void 在创建一个对象使用new操作执行的 主要用于初始化
         * 不能被static、final、synchronized、abstract和native等修饰 构造方法不能被子类继承
          */
        System.out.println("=获取所有公有构造方法=");
        Constructor[] conArray = personClass.getConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        System.out.println("=获取所有构造方法(含私有)=");
        conArray = personClass.getDeclaredConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }

        /**
         * 打印结果
         *   =获取所有公有构造方法=
             public reflect.Person(java.lang.String,java.lang.String)
             public reflect.Person()
             =获取所有构造方法(含私有)=
             public reflect.Person(java.lang.String,java.lang.String)
             private reflect.Person(java.lang.String)
             public reflect.Person()
         */


        /**
         * (二)成员方法
         */
        //获取所有公有方法
        System.out.println("=获取所有公有方法=");
        personClass.getMethods();
        Method[] methodArray = personClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("=获取所有方法 包括私有=");
        methodArray = personClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }
        System.out.println("=获取公有的Person()方法=");
        //getMethod(方法名,参数类型)
        Method m = personClass.getMethod("sayHello", String.class);
        System.out.println(m);
        //先实例化person类
        Object obj = personClass.getConstructor().newInstance();
        //invoke(要调用的对象,实参)
        m.invoke(obj, "hello world");
        System.out.println("=获取私有的say方法="); //getDeclaredMethod(方法名,参数类型)可以用于获取私有方法(也能获取公有) 得解除私有限定
        m = personClass.getDeclaredMethod("say", String.class);
        System.out.println(m);
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj,"say hi"); //调用方法并获取返回值
        System.out.println("返回值:" + result); //null 因为是void方法无返回 如果有返回类型将会有返回值

        /**
         * 打印结果
         * =获取所有公有方法=
         public java.lang.String reflect.Person.getName()
         public void reflect.Person.setName(java.lang.String)
         public java.lang.String reflect.Person.getWork()
         public java.lang.String reflect.Person.getSex()
         public void reflect.Person.setAge(java.lang.String)
         public java.lang.String reflect.Person.getAge()
         public void reflect.Person.setWork(java.lang.String)
         public void reflect.Person.setSex(java.lang.String)
         public void reflect.Person.sayHello(java.lang.String)
         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 final void java.lang.Object.wait() throws java.lang.InterruptedException
         public boolean java.lang.Object.equals(java.lang.Object)
         public java.lang.String java.lang.Object.toString()
         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 reflect.Person.getName()
         public void reflect.Person.setName(java.lang.String)
         public java.lang.String reflect.Person.getWork()
         public java.lang.String reflect.Person.getSex()
         public void reflect.Person.setAge(java.lang.String)
         public java.lang.String reflect.Person.getAge()
         public void reflect.Person.setWork(java.lang.String)
         public void reflect.Person.setSex(java.lang.String)
         private void reflect.Person.say(java.lang.String)
         public void reflect.Person.sayHello(java.lang.String)
         =获取公有的Person()方法=
         public void reflect.Person.sayHello(java.lang.String)
         公有,调用无参的构造方法
         公有方法说的话->hello world
         =获取私有的say方法=
         private void reflect.Person.say(java.lang.String)
         私有方法说的话->say hi
         返回值:null
         */
    }
}

5.反射main方法

这边为了方便写两个类用作测试

//Test
package reflect;
public class Test {
    public static void main(String[] args) {
        System.out.println("执行了Test类里的main方法");
    }
}

//TestMain
package reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Testmain {
public static void main(String[] args) throws NoSuchMethodException,   ClassNotFoundException, InvocationTargetException, IllegalAccessException {
            //1、获取Test对象的字节码
            Class clazz = Class.forName("reflect.Test");
            //2、获取main方法 getMethod(方法名,方法形参类型)
            Method methodMain = clazz.getMethod("main", String[].class);
            //3、调用main方法 
            //第一个参数,对象类型,static静态的可为null,第二个参数是String数组,
            methodMain.invoke(null, (Object)new String[]{}); //输出 执行了Test类里的main方法
        }
    }

6.用于运行配置文件里的内容

本质也是先获取配置文件的内容之后,然后做相应实例化,调用方法等操作,比如说使用quartz需要配置quartz.properties

name = garwer
#经常看到一些jar包的配置文件在properties配置一些jar包路径 可能用于通过反射机制来实例化或者类加载用 也可记录方法名 类中的成员变量等
path = reflect.Person
package reflect;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class testProperties {
    //通过key获取value值
    private static String getVal(String key) throws IOException {
        Properties properties = new Properties();
        FileReader in = new FileReader("src/main/resources/my.properties");//获取输入流 classpath:
        properties.load(in);
        in.close();
        return properties.getProperty(key);
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        System.out.println(testProperties.class.getResource("").getPath());
        System.out.println(getVal("name")); //输出garwer
        String path = getVal("path"); //reflect.Person
        System.out.println(path);
        Object o = Class.forName(path);
        System.out.println(o); //class reflect.Person
    }
}

总结

反射主要用于在运行的时候,获取类的信息并操作它,广泛的用于在设计模式和jdk和各种框架中。

目录
相关文章
|
2月前
|
Java 开发者
重学Java基础篇—Java类加载顺序深度解析
本文全面解析Java类的生命周期与加载顺序,涵盖从加载到卸载的七个阶段,并深入探讨初始化阶段的执行规则。通过单类、继承体系的实例分析,明确静态与实例初始化的顺序。同时,列举六种触发初始化的场景及特殊场景处理(如接口初始化)。提供类加载完整流程图与记忆口诀,助于理解复杂初始化逻辑。此外,针对空指针异常等问题提出排查方案,并给出最佳实践建议,帮助开发者优化程序设计、定位BUG及理解框架机制。最后扩展讲解类加载器层次与双亲委派机制,为深入研究奠定基础。
85 0
|
8天前
|
Java 区块链 网络架构
酷阿鲸森林农场:Java 区块链系统中的 P2P 区块同步与节点自动加入机制
本文介绍了基于 Java 的去中心化区块链电商系统设计与实现,重点探讨了 P2P 网络在酷阿鲸森林农场项目中的应用。通过节点自动发现、区块广播同步及链校验功能,系统实现了无需中心服务器的点对点网络架构。文章详细解析了核心代码逻辑,包括 P2P 服务端监听、客户端广播新区块及节点列表自动获取等环节,并提出了消息签名验证、WebSocket 替代 Socket 等优化方向。该系统不仅适用于农业电商,还可扩展至教育、物流等领域,构建可信数据链条。
|
2月前
|
缓存 Dubbo Java
理解的Java中SPI机制
本文深入解析了JDK提供的Java SPI(Service Provider Interface)机制,这是一种基于接口编程、策略模式与配置文件组合实现的动态加载机制,核心在于解耦。文章通过具体示例介绍了SPI的使用方法,包括定义接口、创建配置文件及加载实现类的过程,并分析了其原理与优缺点。SPI适用于框架扩展或替换场景,如JDBC驱动加载、SLF4J日志实现等,但存在加载效率低和线程安全问题。
理解的Java中SPI机制
|
1月前
|
存储 Java 编译器
Java 中 .length 的使用方法:深入理解 Java 数据结构中的长度获取机制
本文深入解析了 Java 中 `.length` 的使用方法及其在不同数据结构中的应用。对于数组,通过 `.length` 属性获取元素数量;字符串则使用 `.length()` 方法计算字符数;集合类如 `ArrayList` 采用 `.size()` 方法统计元素个数。此外,基本数据类型和包装类不支持长度属性。掌握这些区别,有助于开发者避免常见错误,提升代码质量。
62 1
|
2月前
|
设计模式 缓存 Java
重学Java基础篇—Java对象创建的7种核心方式详解
本文全面解析了Java中对象的创建方式,涵盖基础到高级技术。包括`new关键字`直接实例化、反射机制动态创建、克隆与反序列化复用对象,以及工厂方法和建造者模式等设计模式的应用。同时探讨了Spring IOC容器等框架级创建方式,并对比各类方法的适用场景与优缺点。此外,还深入分析了动态代理、Unsafe类等扩展知识及注意事项。最后总结最佳实践,建议根据业务需求选择合适方式,在灵活性与性能间取得平衡。
106 3
|
2月前
|
安全 IDE Java
重学Java基础篇—Java泛型深度使用指南
本内容系统介绍了Java泛型的核心价值、用法及高级技巧。首先阐述了泛型在**类型安全**与**代码复用**中的平衡作用,解决强制类型转换错误等问题。接着详细讲解了泛型类定义、方法实现、类型参数约束(如边界限定和多重边界)、通配符应用(PECS原则)以及类型擦除的应对策略。此外,还展示了泛型在通用DAO接口、事件总线等实际场景的应用,并总结了命名规范、边界控制等最佳实践。最后探讨了扩展知识,如通过反射获取泛型参数类型。合理运用泛型可大幅提升代码健壮性和可维护性,建议结合IDE工具和单元测试优化使用。
43 1
|
2月前
|
安全 IDE Java
重学Java基础篇—Java Object类常用方法深度解析
Java中,Object类作为所有类的超类,提供了多个核心方法以支持对象的基本行为。其中,`toString()`用于对象的字符串表示,重写时应包含关键信息;`equals()`与`hashCode()`需成对重写,确保对象等价判断的一致性;`getClass()`用于运行时类型识别;`clone()`实现对象复制,需区分浅拷贝与深拷贝;`wait()/notify()`支持线程协作。此外,`finalize()`已过时,建议使用更安全的资源管理方式。合理运用这些方法,并遵循最佳实践,可提升代码质量与健壮性。
81 1
|
2月前
|
缓存 运维 Java
Java静态代码块深度剖析:机制、特性与最佳实践
在Java中,静态代码块(或称静态初始化块)是指类中定义的一个或多个`static { ... }`结构。其主要功能在于初始化类级别的数据,例如静态变量的初始化或执行仅需运行一次的初始化逻辑。
89 4
|
4月前
|
存储 移动开发 算法
【潜意识Java】Java基础教程:从零开始的学习之旅
本文介绍了 Java 编程语言的基础知识,涵盖从简介、程序结构到面向对象编程的核心概念。首先,Java 是一种高级、跨平台的面向对象语言,支持“一次编写,到处运行”。接着,文章详细讲解了 Java 程序的基本结构,包括包声明、导入语句、类声明和 main 方法。随后,深入探讨了基础语法,如数据类型、变量、控制结构、方法和数组。此外,还介绍了面向对象编程的关键概念,例如类与对象、继承和多态。最后,针对常见的编程错误提供了调试技巧,并总结了学习 Java 的重要性和方法。适合初学者逐步掌握 Java 编程。
84 1
|
5月前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。