Java反射(3万字总结,最后的最后我想要一个赞,您的支持是我的动力)

Posted 老赖的小弟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射(3万字总结,最后的最后我想要一个赞,您的支持是我的动力)相关的知识,希望对你有一定的参考价值。

文章目录

前言

一直对于反射很感兴趣,原本以为要到框架才能接触到,但往往计划没有变化快,今天终于接触到了心心念念的——反射了。year…

一、关于反射与注解的那些话(了解)

近十几年来,在Java和android开发领域中涌现出许多优秀的框架,比如:Spring、Hibernate、Mybatis、Retrofit、Afinal、OKHttp、ButterKnife等等。这些框架的出现极大地简化了开发流程,提高了工作效率。在项目开发的过程中我们主要是使用这些轮子完成项目,很难有时间去顾及框架的内部实现。

1、反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。
2、反射可以在一个类运行的时候获取类的信息的机制,可以获取在编译期不可能获得的类的信息。
3、对于任意一个对象,都能调用它的任意一个方法和属性。
4、因为类的信息是保存在Class对象中的,而这个Class对象是在程序运行时被类加载器(ClassLoader)动态加载的。

二、编程语言类型

1.静态类型语言

静态类型语言(Statically Typed Language)也称为强类型语言。在静态语言中,在编译时确定变量的数据类型。所以,绝大多数静态类型语言要求:先声明变量(即确定变量类型)后再使用变量。

常见的静态类型语言有:Java、 C、C++等等

2.动态类型语言

动态类型语言(Dynamic programming Language)也称为弱类型语言。在动态类型语言中,程序在运行时可以改变其结构;例如:改变变量原有的数据类型、新的函数被引进,已有的函数被删除等在结构上的变化。

常见的动态类型语言有:javascriptphp、Ruby等等。

2.1Java与动态性

Java程序的执行有两个阶段:
1、编译
2、运行
关于Java的编译、运行请参考:Java中字节码及其优势

在编译阶段会检查基本语法是否正确、变量的类型及其使用是否正确。程序在通过编译之后生成与之对应的字节码文件.class。JVM调用代码时,程序处于运行时状态。虽然说Java语言是静态类型语言,但是它在运行时借助反射从而具有了一定的动态性。所以,也有人说Java是"准动态类型语言"。

三、反射

1.反射的定义

Java的反射(Reflection)机制是指在程序运行时动态获取程序信息和动态调用对象的功能。反射是Java实现动态性的关键,其主要作用如下:

1、在运行时判断任意一个对象所属的类
2、在运行时构造任意一个类的对象
3、在运行时判断任意一个类所具有的成员变量和方法
4、在运行时调用任意一个对象的成员变量和方法
5、在运行时获取泛型信息
6、在运行时处理注解
7、在运行时生成动态代理

2.反射的应用场景

1、插件化编程
2、服务端编程
3、无.java源文件
4、获取类的所有信息
5、调用私有属性和方法

3.关于反射学习的进程的科学安排

1、ClassLoader
2、Class
3、Reflection API

四、类加载器

1.类加载器的分类

Java的类加载器有如下四种:

1、启动类加载器(Bootstrap Classloader)
2、扩展类加载器(Extension ClassLoader)
3、应用程序类加载器(Application Classloader)
4、自定义类加载器(User Classloader)

关于Java类加载器的关系请参考:Java类加载器

1.1启动类加载器(Bootstrap Classloader)

启动类加载器(Bootstrap Classloader)并不是java.lang.ClassLoder的子类,而是使用C/C++实现的。所以,无法通过Java代码获取启动类加载器。

代码如下(获取启动类加载器(Bootstrap Classloader) 代码演示):

public class GetBootstrapClassloader 

	public static void main(String[] args) 
		String string = "hello 反射!";
		Class<? extends String> clazz = string.getClass();
		ClassLoader classLoader = clazz.getClassLoader();
		System.out.println(classLoader);
	

代码如下(获取启动类加载器(Bootstrap Classloader) 输出):

null

1.2扩展类加载器(Extension ClassLoader)

扩展类加载器(Extension ClassLoader)由sun.misc.Launcher$ExtClassLoader实现,它是java.lang.ClassLoader的子类。该类加载器负责加载Java的扩展库JAVA_HOME/jre/lib/ext/*.jar或者java.ext.dirs路径下的内容。
代码如下(获取扩展类加载器(Extension ClassLoader) 代码演示):

import java.io.File;
import java.util.StringTokenizer;

public class GetExtensionClassLoader 

	public static void main(String[] args) 
		String string = System.getProperty("java.ext.dirs");
		StringTokenizer stringTokenizer = new StringTokenizer(string, File.pathSeparator);
		int countTokens = stringTokenizer.countTokens();
		File[] extDirs = new File[countTokens];
		for(int i = 0;i<countTokens;i++) 
			String nextToken = stringTokenizer.nextToken();
			extDirs[i] = new File(nextToken);
			System.out.println(extDirs[i]);
		
	

代码如下(获取启动类加载器(Bootstrap Classloader) 输出):

C:\\Program Files\\Java\\jdk1.8.0_162\\jre\\lib\\ext
C:\\Windows\\Sun\\Java\\lib\\ext

1.3应用程序类加载器(Application Classloader)

应用程序类加载器(Application Classloader)由sun.misc.Launcher$AppClassLoader实现,它是java.lang.ClassLoader的子类。该类加载器负责加载Java应用程序类路径classpath或者java.class.path下的内容。也就是说:平常,我们在项目中自己写的类就是由应用程序类加载器加载进内存的。

代码如下(获取应用程序类加载器(Application Classloader) 代码演示):

public class GetApplicationClassLoader 

	public static void main(String[] args) throws Exception 
		Class<?> clazz = Class.forName("com.classloader.Student");
		ClassLoader classLoader = clazz.getClassLoader();
		System.out.println(classLoader);

	

代码如下(获取应用程序类加载器(Application Classloader) 输出):

sun.misc.Launcher$AppClassLoader@6d06d69c

五、Class类

类加载器将类的.class文件加载进内存之后会在堆内存的方法区中产生Class类型的对象;该对象包含了类的所有结构信息。一个类有且只有一个Class对象,相同的类具有相同的Class对象 。我们只要获得了Class对象,就可以全面剖析一个类并可创建该类的对象且调用其方法。

1.在运行时类和接口对应的Class对象

代码如下(在运行时类和接口对应的Class对象 代码演示):

public class GetClass 
	public static void main(String[] args) 
		// 获取String类对应的class对象
		Class<String> clazz01 = String.class;
		// 获取Thread接口对应的class对象
		Class<Thread> clazz02 = Thread.class;
		System.out.println(clazz01);
		System.out.println(clazz02);
	


代码如下(在运行时类和接口对应的Class对象 输出):

class java.lang.String
class java.lang.Thread

2.在运行时枚举和注释(即注解)也存在Class对象

代码如下(在运行时枚举和注释对应的Class对象 代码演示):

import java.lang.annotation.ElementType;

public class GetClass 
	public static void main(String[] args) 
		// 获取ElementType枚举对应的class对象
		Class<?> clazz01 = ElementType.class;
		// 获取Overridezhu注解对应的class对象
		Class<?> clazz02 = Override.class;
		System.out.println(clazz01);
		System.out.println(clazz02);
	

代码如下(在运行时枚举和注释对应的Class对象 输出):

class java.lang.annotation.ElementType
interface java.lang.Override

3.在运行时,数组也存在相应的Class对象

代码如下(在运行时数组对应的Class对象 代码演示):

public class GetClass 
	public static void main(String[] args) 
		// 获取一维数组对应的class对象
		Class<?> clazz01 = int[].class;
		// 获取二维数组对应的class对象
		Class<?> clazz02 = int[][].class;
		System.out.println(clazz01);
		System.out.println(clazz02);
	

代码如下(在运行时枚举和注释对应的Class对象 输出):

class [I
class [[I

4. 在运行时,Java基本数据类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象

代码如下(在运行时,Java基本数据类型关键字 void 也表示为 Class 对象 代码演示):

public class GetClass 
	public static void main(String[] args) 
		// 获取int的class对象
		Class<?> clazz01 = int.class;
		// 获取void对应的class对象
		Class<?> clazz02 = void.class;
		// 获取boolean对应的class对象
		Class<?> clazz03 = boolean.class;
		System.out.println(clazz01);
		System.out.println(clazz02);
		System.out.println(clazz03);
	

代码如下(在运行时,Java基本数据类型关键字 void 也表示为 Class 对象 输出):

int
void
boolean

5. 获取Class对象的方式小结

5.1 类型名.class

该方式适用于任意类型获取Class对象

代码如下(示例):

	// 通过类型名.class获取class对象
	public static void test01() 
		Class<?> clazz01 = int.class;
		Class<?> clazz02 = String.class;
		System.out.println(clazz01);
		System.out.println(clazz02);
	

代码如下(输出):

int
class java.lang.String

5.2 引用类型对象.getClass( )

因为只有引用类型才有对象;所以,该方式适用于引用类型获取Class对象

代码如下(示例):

	// 通过引用类型对象.getClass()获取Class对象
	public static void test02() 
		String string = "时间带带";
		Class<? extends String> clazz01 = string.getClass();
		Object object = new Object();
		Class<? extends Object> clazz02 = object.getClass();
		System.out.println(clazz01);
		System.out.println(clazz02);
	

代码如下(输出):

class java.lang.String
class java.lang.Object

5.3 Class.forName(“类的全限定名”)

该方式适用于除数组以外的引用类型数据获取Class对象

代码如下(示例):

	// 通过Class.forName("类的全限定名")获取Class对象
	public static void test03() 
		Class<?> clazz01;
		try 
			clazz01 = Class.forName("java.lang.String");
			System.out.println(clazz01);
		 catch (ClassNotFoundException e) 
			// TODO Auto-generated catch block
			e.printStackTrace();
		
	

代码如下(输出):

class java.lang.String

5.4 类加载器对象.loadClass(“类的全限定名”)

该方式适用于除数组以外的引用类型数据获取Class对象。

代码如下(示例):

	public static void test04() throws Exception 
		ClassLoader classLoader = ClassLoader.getSystemClassLoader();
		Class<?> loadClass = classLoader.loadClass("java.lang.String");
		System.out.println(loadClass);
	

代码如下(输出):

class java.lang.String

六、Reflection API

1. 运行时获取类加载器

1.1 API

获取类加载器

ClassLoader java.lang.Class.getClassLoader()

1.2 运行时获取类加载器代码演示

代码如下(示例):

public class GetClassLoader 

	public static void main(String[] args) 
		// 获取String类的类加载器
		Class<?> clazz01 = String.class;
		ClassLoader classLoader01 = clazz01.getClassLoader();
		// 获取Test1类的类加载器
		Class<?> clazz02 = Test1.class;
		ClassLoader classLoader02 = clazz02.getClassLoader();
		System.out.println(classLoader01);
		System.out.println(classLoader02);
	

代码如下(输出):

null
sun.misc.Launcher$AppClassLoader@6d06d69c

2. 运行时获取类的全限定名和包名

2.1 API

获取类的全限定名

String java.lang.Class.getName()

如果此类对象表示的是非数组类型的引用类型,则返回包.类名

如果此类对象表示一个基本类型或 void,则返回该基本类型或 void 所对应的 Java 语言关键字相同的字符串

如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 ‘[’ 字符加元素类型名。其中,元素类型名的编码如下:

ElementType Encoding
byteB
shortS
intI
longL
floatF
doubleD
booleanZ
charC
class or interfaceLclassname

获取类所属包

Package java.lang.Class.getPackage()

获取类所属包的名称

String java.lang.Package.getName()

2.1 运行时获取类的全限定名和包名代码演示

代码如下(示例):

public class GetNameTest 

	public static void main(String[] args) 
		// 获取String类的class对象
		Class<?> clazz01 = String.class;
		// 获取String类的全限定名
		String name = clazz01.getName();
		// 获取String类的包名
		String packageName = clazz01.getPackage().getName();
		System.out.println("全限定名为:"+name);
		System.out.println("包名为:"+packageName);
	

代码如下(输出):

全限定名为:java.lang.String
包名为:java.lang

3. 运行时获取类的修饰

3.1 API

获取类或接口的修饰符

int java.lang.Class.getModifiers()

获取类或接口的修饰符的字符串表示形式

String java.lang.reflect.Modifier.toString(int mod)

判断类或接口是否被public修饰

boolean java.lang.reflect.Modifier.isPublic(int mod)

3.2 运行时获取类的修饰代码演示

代码如下(示例):

import java.lang.reflect.Modifier;

public class Demo 

	public static void main(String[] args) 
		// 获取String类的class对象
		Class<?> clazz01 = String.class;
		// 获取String类的修饰符
		int modifiers = clazz01.getModifiers();
		// 将String类的修饰符转成字符串
		String modifierString = Modifier.toString(modifiers);
		// 判断String类是否被public修饰
		boolean public1 = Modifier.isPublic(modifiers);
		System.out.println(modifierString);
		System.out.println(public1);

	


代码如下(输出):

public final
true

4. 运行时获取类的基本信息

4.1 API

获取类的父类

Class<? super Object> java.lang.Class.getSuperclass()

获取类实现的接口

Class<?>[] java.lang.Class.getInterfaces()

4.2 运行时获取类的基本信息代码演示

代码如下(示例):

import java.lang.reflect.Modifier;

public class Demo 

	public static void main(String[] args) 
		// 获取String类的class对象
		Class<?> clazz01 = String.class;
		// 获取String类的修饰符
		int modifiers = clazz01.getModifiers();
		// 将String类的修饰符转成字符串
		String modifierString = Modifier.toString(modifiers);
		// 判断String类是否被public修饰
		boolean public1 = Modifier.isPublic(modifiers);
		// 获取String类的父类
		Class<?> superclass = clazz01.getSuperclass();
		// 获取String类实现的接口
		Class<?>[] interfaces = clazz01.getInterfaces();
		for(Class<?> c:interfaces) 
			System.out.println("String类实现的接口:"+c);
		
		System.out.println("String类的父类:"+superclass);
		System.out.println("String类的修饰符:"+modifierString);
		System.out.println("关于String类的修饰是否被public修饰:"+public1);

	


代码如下(输出):

String类实现的接口:interface java.io.Serializable
String类实现的接口:interface java.lang.Comparable
String类实现的接口:interface java.lang.CharSequence
String类的父类:class java.lang.Object
String类的修饰符:public final
关于String类的修饰是否被publ

以上是关于Java反射(3万字总结,最后的最后我想要一个赞,您的支持是我的动力)的主要内容,如果未能解决你的问题,请参考以下文章

❤️最后的大爆发❤️五万字总结SpringMVC教程——三部曲封神之作(建议收藏)

Spring IoC容器初始化源码—populateBeaninitializeBean填充Bean字段反射和setter方法依赖注入以及IoC容器初始化总结四万字

万字总结之设计模式(扫盲篇)

##(⊙o⊙)值得收藏的JavaSE万字进阶版(⊙o⊙)##JavaSE 高级反射-动态代理-设计模式-JVM篇

动态规划此一篇就够了 万字总结

Java反射调用与面向对象结合使用产生的惊艳