反射
反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。
从java基础的阶段我们了解到,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
package com.qijie.java;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println("我是一个人");
}
public void display(String nation){
System.out.println("我的国籍是:"+nation);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
import org.junit.Test;
public class TestReflection {
// 有了反射可以通过反射创建一个类的对象并调用其中的结果
// 有反射前 我们如何创建一个类的对象并对用其中的方法或属性
@Test
public void Test1(){
Person person=new Person();
person.setAge(12);
person.setName("石晨霖");
System.out.println(person);
person.show();
person.display("HK");
}
}
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
前提须知:
JDK提供的反射API
Class clz = Class.forName("com.atqijie.reflect.SCL");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
仔细观察一下上述代码直接的异同,我们从结果来看都获取了方法,但是获取的方式不同。
第一种的方式是我们再java基础阶段最普遍的创建对象 获取方法,属于在运行的时候我们已经知道了这到底是什么类(Person)
第二种我们在运行时通过字符串值才知道了运行的类是(SCL)
1 调用运行时类本身的。class属性
*/
@Test
public void Test4() throws ClassNotFoundException {
Class clazz1= Person.class;
System.out.println(clazz1.getName());
Class clazz2=String.class;
System.out.println(clazz2.getName());
2第二种方式 通过运行时类的对象来获取
Person p =new Person();
Class clazz3=p.getClass();
System.out.println(clazz3.getName());
3第三种通过Class的静态方法ForName来获取
/第三种:反射的动态性就是下面两行代码不用动更具你传进来的不同 来创建的对象也不一样 调用的也不一样
Class clazz4= Class.forName(className);//获取包名路径
// clazz4.newInstance();通过newInstance创建实例
System.out.println(clazz4.getName());
}
public class TestConstructor {
@Test
public void Test1() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// 第三种通过Class的静态方法来获取
String className="com.qijie.java.Person";
Class clazz=Class.forName(className);
// 创建对应的运行时类的对象 使用newInstance方法 实际上就是调用了运行时类的空参构造器
// 要想创建成功 要求对应的运行类要有空参的构造器 构造器的权限修饰符的权限要足够
Object object= clazz.newInstance();//默认的返回时object类型的 newInstance调用的就是空参的构造器
Person p=(Person) object;
System.out.println(p);
}
}
我们在获取类之后需要创建对应的对象 所以需要newInstance方法 这个时候就需要调用到空的构造器且构造器的权限需要够大
clazz.newInstance 创建了对象 但是由于我们不知道这个对象时哪个类型 所以我们定义为Object类型
再强制转换为我们获取得到的Person类 最后调用。
获取实现的接口 和获取注解
public class TestInterface {
@Test
// 获取实现的接口
public void Test(){
Class clazz=Person.class;
Class[] interfaces= clazz.getInterfaces();
for(Class i:interfaces){
System.out.println(i);
}
}
// 获取注解
@Test
public void test(){
Class clazz=Person.class;
Annotation[] annotations=clazz.getAnnotations();
for(Annotation annotation:annotations){
}
根据 Class 对象实例获取 Constructor (结构体)对象
Constructor appleConstructor = clz.getConstructor();
要调用某一个方法,则需要经过下面的步骤:
- 获取方法的 Method 对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);
- 利用 invoke 方法调用方法
setPriceMethod.invoke(appleObj, 14);
举例:
// 调用运行时类中指定的方法
@Test
public void Test3() throws Exception {
Class clazz=Person.class;
// getMethod运行时声明为public的指定的方法
Method method1=clazz.getMethod("show");//有形参需要写
Method method2=clazz.getMethod("toString");
Person p= (Person) clazz.newInstance();
// 调用指定的方法是invoke 有形参的话需要些具体参数 否则包异常 方法是有返回值的 如果没有返回值的话需要 使用Object
Object returnValue1= method1.invoke(p);//我是一个人
Object returnVulue2=method2.invoke(p);
System.out.println(returnValue1);//因为没有返回值所以是null
System.out.println(returnVulue2);//Perosn name=null age=0
// 对于运行类静态方法的调用
Method method3=clazz.getMethod("info");
method3.invoke(Person.class);
//getDeclaredMehtod(String。。。)获取运行时类中声明了的指定的方法
Method method4= clazz.getDeclaredMethod("display", String.class,Integer.class);//首先获取方法
//其次调用方法 由于此方法时私有属性苏哦一需要setAccessible
method4.setAccessible(true);
Object Value=method4.invoke(p,"CNN",10);//我的国际时 CNN 10
System.out.println(Value);//输出的返回值10
}
//调用指定的构造器 创建运行时类的对象 反射使用的时newInstance来通过空构造器来创建类的对象
// 所以我们一般提前设置好空构造器 但是没有空构造器我们如何来创建对象 我们可以调用指定的构造器来创建
@Test
public void Test4() throws Exception {
String className="com.qijie.java.Person";
Class clazz=Class.forName(className);
Constructor constructor=clazz.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
Person person2=(Person) constructor.newInstance("scl" ,21);
System.out.println(person2);
}
}
注意一点的是,在我们获取方法时候,很多时候方法private而非public,此时我们使用getMethod()将无法获取方法,需要使用getDecaredMethod()。且设置属性
反射与new的区别
反射和new都是创建对象实例的,
不同的是
new对象无法调用该类里面私有private的属性,而反射可以调用类中private的属性
new属于静态编译。就是在编译的时候把所有的模块都确定,如果有添加或删除某些功能,需要重新编译。但系统不可能一次就把把它设计得很完美,当发现需要更新某些功能时,采用静态编译的话,需要把整个程序重新编译一次才可以实现功能的更新。也就是说,用户需要把以前的软件卸载了,再重新安装才会重新编译!这样的系统耦合严重,难以扩展
反射属于动态编译。在运行时确定类型并创建对象,通过反射指定模板,动态的向模板中传入要实例化的对象。动态编译最大限度发挥了Java的灵活性,体现了多态的应用,有以降低类之间的藕合性。其中spring中ioc的核心就是利用了反射解耦合。
反射在ioc的应用
原来我们创建类和调用
public interface Fruit {
public void Love();
}
实现类1
public class FruitImpl1 implements Fruit{
@Override
public void Love() {
System.out.println("大雄喜欢静香");
}
}
实现类2
public class FruitImpl2 implements Fruit{
@Override
public void Love() {
System.out.println("胖虎喜欢静香、。。。。");
}
}
创建工厂类
public class FruitFactory {
public static Fruit getInstance(String FruitName){
Fruit f=null;
if("daxiong".equals(FruitName)){
f=new FruitImpl1();
}
if("panghu".equals(FruitName)){
f=new FruitImpl2();
}
return f;
}
}
class hello{
public static void main(String[] a){
Fruit f=FruitFactory.getInstance("panghu");
f.Love();
}
}
上面的缺点就是如果我需要修改一个类 其中的类也需要随之更改,耦合度太高 所谓牵一发而动全身
这时候我们就需要联想到我们之前所学到的有关ioc容器的知识
public class FruitFactory {
public static Fruit getInstance(String ClassName) throws Exception {
Fruit f=null;
f=(Fruit)Class.forName(ClassName).newInstance();
return f;
}
}
class hello {
public static void main(String[] args) throws Exception {
Fruit f = FruitFactory.getInstance("FruitImpl1");
if (f != null) {
f.Love();
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?