Java反射(3万字总结,最后的最后我想要一个赞,您的支持是我的动力)
Posted 老赖的小弟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射(3万字总结,最后的最后我想要一个赞,您的支持是我的动力)相关的知识,希望对你有一定的参考价值。
文章目录
- 前言
- 一、关于反射与注解的那些话(了解)
- 二、编程语言类型
- 三、反射
- 四、类加载器
- 五、Class类
- 六、Reflection API
前言
一直对于反射很感兴趣,原本以为要到框架才能接触到,但往往计划没有变化快,今天终于接触到了心心念念的——反射了。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)也称为弱类型语言。在动态类型语言中,程序在运行时可以改变其结构;例如:改变变量原有的数据类型、新的函数被引进,已有的函数被删除等在结构上的变化。
常见的动态类型语言有:javascript、 php、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)
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 语言关键字相同的字符串
如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 ‘[’ 字符加元素类型名。其中,元素类型名的编码如下:
Element | Type Encoding |
---|---|
byte | B |
short | S |
int | I |
long | L |
float | F |
double | D |
boolean | Z |
char | C |
class or interface | Lclassname |
获取类所属包
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容器初始化总结四万字