zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Java 反射

2023-09-14 09:04:25 时间

1、什么是反射?

官方:在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。

仅仅从概念上我们还无法真正了解反射,通过下面的学习,我相信你会突然对反射机制恍然大悟。那么就让我们一起去瞧瞧吧!

2、Class 类的使用

(1)首先大家都知道在Java中,万物皆对象。好,那么成员变量、成员方法、静态成员变量等等这些都是不是对象呢?
答案:是的。

(2)那么类是对象吗?,是的话又是谁的对象呢?
答案:类是对象,类是 java.lang.Class类的实例对象。
例如:

public class Main {
}

我们就可以认为 Main这个类是 java.lang.Class 类的实例对象。

(3)相信大家都知道,如何获取某个类的对象
通过这段代码,我们就轻松的获得了 Foo 这个类的实例对象 f1

public class ClassDemo1 {
	public static void main(String[] args) {
		Foo f1 = new Foo();
	}
}
class Foo{}

(4)我们说过,类是对象同时它是java.lang.Class类的实例对象。那如何表示Class的实例对象呢?
有三种表示方式
这里我们拿 Foo这个类来举例,当然也可以拿任意其他类,因为所有的类都是Class类的实例对象。

  • 方式一
Class c1 = Foo.class;

从方式一中,可以得到一个结论:
任何一个类都有一个隐含的静态成员变量。

  • 方式二
Foo f1 = new Foo();
Class c2 = f1.getClass();
  • 方式三

Class.forName(“类的全称”)

try {
		Class c3 = Class.forName("com.wangrui.reflect.Foo");
	} catch (ClassNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

(5)c1、c2 对象,我们称之为该类的类类型。
如果拿Foo 来说,c1、c2就是Foo 的类类型。那么我就敢说 c1、c2、c3都是同一个对象。如果不了解 == 和equals比较的同学建议看我的这篇博客《Java ==和equals有什么区别?》

System.out.println(c1==c2);
System.out.println(c2 == c3);

运行结果:
true
true

(6)通过该类的类类型创建该类的对象实例,并且调用该类的某个方法

public class ClassDemo1 {
	public static void main(String[] args) {
		Class c1 = Foo.class;	
		try {
			Foo foo = (Foo) c1.newInstance();
			foo.start();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
class Foo{
	public void start() {
		System.out.println("未来的你会感谢此刻的自己");
	}
}

运行结果:
未来的你会感谢此刻的自己

3、动态加载类

(1)静态加载类、动态加载类

静态加载类:编译时可加载的类
动态加载类:运行时刻加载的类

我们之前提到的 Class.forName(“类的全称”),不仅表示类的类类型,同时还代表了动态加载类。

(2)为什么要有动态加载类
假如现在有一个场景:要求运行 Word里的start()方法

源代码项目结构
在这里插入图片描述
word.java

public class Word {
	public static void start() {
		System.out.println("word");
	}
}

Office.java

在这里插入图片描述
可以看到 Excel处报错,无法执行程序,很明显是因为 Excel 类不存在。那么现在我的要求是,Word 存在,程序就能执行Word。那么按照这种写法很显然就会有问题,因为:

new 创建对象 是静态加载类,在编译时刻就需要加载所有的可能使用到的类

所以 Excel不存在,自然就通过不了编译。如果以后我们有一百个功能,只要1个功能有问题,其他99个就都用不了了。

(3)通过动态加载类就能解决上面的问题

新建:OfficeBetter.java

public class OfficeBetter {
	public static void main(String[] args) {
		try {
			//动态加载类,在运行时刻加载
			Class c = Class.forName("com.wangrui.reflect.Word");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

我们都知道,通过类的类类型就可以创建该类的对象,但是我们不能如下这么写:

			Word w = (Word) c.newInstance();	//c加载Excel就会报错
			Excel e = (Excel) c.newInstance();	//c加载Word就会报错

这种时候我们就需要给他们制定统一标准
我们可以让 Word 和 Excel都属于 OfficeAble这个标准,如下

OfficeAble oa = (OfficeAble) c.newInstance();

详细步骤:
新增:OfficeAble.java 接口

public interface OfficeAble {
	public void start();
}

让Word 遵循这个标准,Word.java

public class Word implements OfficeAble{
	public void start() {
		System.out.println("word");
	}
}

回到 OfficeBetter.java

public class OfficeBetter {
	public static void main(String[] args) {
		try {
			//动态加载类,在运行时刻加载
			Class c = Class.forName("com.wangrui.reflect.Word");
			OfficeAble oa = (OfficeAble) c.newInstance();
			oa.start();
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}
}

运行结果:
在这里插入图片描述
这样就就解决了当 Excel不存在的时候,我们也能使用 Word这个功能。

如果将来我们增加 Excel功能,就可以

public class Excel implements OfficeAble{
	@Override
	public void start() {
		System.out.println("excel 启动啦");
	}
}

OfficeBetter.java

Class c = Class.forName("com.wangrui.reflect.Excel");

运行结果:
在这里插入图片描述

如果将来还需要增加 PPT功能,也是一个同上一个道理,这样就避免了传统写法的错误,一个功能用不了就都用不了。

4、获取方法信息

(1)回顾一下前面

  • 表示:double这个类型 的类类型
Class c1 = double.class;
  • 表示:Double这个类 的类类型
Class c2 = Double.class;
  • 拓展1:void关键字也有它的类类型
Class c3 = void.class;
  • 拓展2:String 类的类类型,也表示String 类的字节码
Class c4 = String.class;

(2)获取类的名称、方法对象、


5、获取成员变量构造函数信息

6、方法反射的基本操作

7、通过反射了解集合泛型的本质