夯实Java基础(二十一)——Java反射机制
1、反射机制概述
Java反射机制是指程序在运行状态中,对于任何一个类,我们都能够知道这个类的所有属性和方法(包括private、protected等)。对于任何一个对象,我们都能够对它的属性和方法进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。
在程序运行时,当一个类加载完成之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只会对应一个Class对象,绝对不会产生第二个),这个Class对象就包含了完整的类的结构信息。我们创建该类的对象后就可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构。所以,我们形象的称之为:反射。这么说非常的抽象,不能理解,但是通过后面代码的体现就会变得容易理解,暂时先了解一下即可。
到后面还会学习到框架的知识(如Spring、Hibernate等等),框架的组成结构可以理解为这个公式:框架=反射+注解+设计模式。其中反射就是框架的灵魂所在。所以反射在Java的学习中非常重要。
那么Java反射机制主要提供的功能有哪些:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时获取泛型信息
- 在运行时处理注解
- 生成动态代理
java反射API中常用的类介绍:
- java.lang.Class类:代表一个类
- java.lang.reflect.Field类:代表类的成员变量(成员变量也称为类的属性)
- java.lang.reflect.Method类:代表类的方法
- java.lang.reflect.Constrctor类:代表类的构造方法
2、关于Class类
Class类是用来描述类的类,它是一个十分特殊的类,没有构造方法。Class对象的加载过程如下:当程序运行时,我们编写的每一个类,编译完成后,都会生成 类名.class 文件,当我们我们new对象或者类加载器加载的时候,JVM就会加载我们的 类名.class 文件,并且加载到内存中,然后在将 类名.class 文件读取并且同时会生成该类唯一的一个Class对象,用于表示该类的所有信息。
Class代表一个类,在运行的Java应用程序中表示类或接口。在Class类中提供了很多有用的方法,这里对他们简单的介绍。此处参考
1、获得类相关的方法:
- asSubclass(Class<U> clazz):把传递的类的对象转换成代表其子类的对象
- Cast:把对象转换成代表类或是接口的对象
- getClassLoader():获得类的加载器
- getClasses():返回一个数组,数组中包含该类中所有公共类和接口类的对象
- getDeclaredClasses():返回一个数组,数组中包含该类中所有类和接口类的对象
- forName(String className):根据类名返回类的对象
- getName():获得类的完整路径名字
- newInstance():创建类的实例,它调用的是此类的默认构造方法(没有默认无参构造器会报错)
- getPackage():获得类的包
- getSimpleName():获得类的名字
- getSuperclass():获得当前类继承的父类的名字
- getInterfaces():获得当前类实现的类或是接口
2、获得类中属性相关的方法:
- getField(String name):获得某个公有的属性对象
- getFields():获得所有公有的属性对象
- getDeclaredField(String name):获得某个属性对象
- getDeclaredFields():获得所有属性对象
3、获得类中注解相关的方法:
- getAnnotation(Class<A> annotationClass):返回该类中与参数类型匹配的公有注解对象
- getAnnotations():返回该类所有的公有注解对象
- getDeclaredAnnotation(Class<A> annotationClass):返回该类中与参数类型匹配的所有注解对象
- getDeclaredAnnotations():返回该类所有的注解对象
4、获得类中构造器相关的方法:
- getConstructor(Class...<?> parameterTypes):获得该类中与参数类型匹配的公有构造方法
- getConstructors():获得该类的所有公有构造方法
- getDeclaredConstructor(Class...<?> parameterTypes):获得该类中与参数类型匹配的构造方法
- getDeclaredConstructors():获得该类所有构造方法
5、获得类中方法相关的方法:
- getMethod(String name, Class...<?> parameterTypes):获得该类某个公有的方法
- getMethods():获得该类所有公有的方法
- getDeclaredMethod(String name, Class...<?> parameterTypes):获得该类某个方法
- getDeclaredMethods():获得该类所有方法
6、类中其他重要的方法:
- isAnnotation():如果是注解类型则返回true
- isAnnotationPresent(Class<? extends Annotation> annotationClass):如果是指定类型注解类型则返回true
- isAnonymousClass():如果是匿名类则返回true
- isArray():如果是一个数组类则返回true
- isEnum():如果是枚举类则返回true
- isInstance(Object obj):如果obj是该类的实例则返回true
- isInterface():如果是接口类则返回true
- isLocalClass():如果是局部类则返回true
- isMemberClass():如果是内部类则返回true
Class类中的方法还有有很多,这里只列举了部分,需要学习更多的可以自行去查看Class的API文档。
注意:Class并不是只有普通类或接口才能获取,其中基本数据类型、数组、枚举、注解、void等都可以获取其Class对象,甚至Class这个类本身也可以获取Class对象,简单举例:
Class<Object> c1 = Object.class;
Class<String> c2 = String.class;
Class<Integer> c3 = int.class;
Class<int[]> c4 = int[].class;
Class<int[][]> c5 = int[][].class;
Class<ElementType> c6 = ElementType.class;
Class<Override> c7 = Override.class;
Class<Class> c8 = Class.class;
Class<Void> c9 = void.class; int arr[]=new int[10];
int arr1[]=new int[100];
Class<? extends int[]> c10 = arr.getClass();
Class<? extends int[]> c11 = arr1.getClass();
//只要元素类型和维度相同,就是同一个Class
System.out.println(c10==c11);
3、获取Class的几种方式
既然反射机制一定会用到Class这个类,那么就必须先获取它,获取Class对象有四种方式。注意:反射这里所有举例都用Person类来作为演示,所以先创建一个Person类,后面会一直用这个。
public class Person {
//公共的成员变量
public String name;
//default成员变量
int age;
//私有的成员变量
private String address;
public Person() {
}
//私有构造方法
private Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
getter/setter方法省略
//公共方法
public void display1(){
System.out.println("公共方法...");
}
//私有方法
private void display2(){
System.out.println("私有方法...");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
四种方式分别是:
①、通过Object类中的getClass方法获取。这种方式要先创建类的对象,这样再使用反射就多此一举了,不推荐。
②、通过类名.class 直接获取。这种方式需要导入相应类的包,依赖性较强,不推荐。
③、使用Class类中静态方法forName(String className)获取。所以这种方式最常用。
④、使用类加载器ClassLoader来获取。这种方式了解即可,用的很少。
代码演示如下:
public class Reflection {
public static void main(String[] args) throws ClassNotFoundException {
//第一种方式:getClass
Person person = new Person();
Class<? extends Person> clazz1 = person.getClass();
System.out.println("getClass获取:"+clazz1);
//第二种方式:类名.class
Class<Person> clazz2 = Person.class;
System.out.println("类名.class获取:"+clazz2);
//第三种方式:forName
try {
Class<?> clazz3 = Class.forName("com.thr.Person");
System.out.println("forName()方法获取:"+clazz3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第四种方式:类加载器ClassLoader
ClassLoader classLoader = Reflection.class.getClassLoader();
Class<?> clazz4 = classLoader.loadClass("com.thr.Person");
System.out.println("类加载器ClassLoader获取:"+clazz4);
//可以发现返回都是true,说明引用的同一个对象
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz4);
}
}

这四种方式推荐使用Class类中的静态方法forName(String className)来获取。而且无论使用哪种方式获取,结果都是同一个类。
4、获取成员变量
在我们正常创建对象的实例时,其内部的私有元素是不能访问的,但是使用反射则可以轻易的获取,前面创建的Person类中属性、方法和构造器都有被private关键字所修饰。所以接下来演示一下怎么获取成员变量、方法和构造器。
获取属性使用到的是Field这个类,它内部的方法主要是获取、设置某个属性的方法,例如:getXXX()、setXXX();
public class Reflection {
public static void main(String[] args) throws Exception {
//获取Person的Class对象
Class<?> clazz = Class.forName("com.thr.Person");
//1、通过反射获取所有public的成员变量,private、protected、default是不能获取到的
Field[] fields1 = clazz.getFields();
for (Field field : fields1) {
System.out.println(field.getName());
}
System.out.println("------------------------");
//2、通过反射单个获取public成员变量
Field name = clazz.getField("name");
System.out.println(name);
Field age = clazz.getDeclaredField("age");
System.out.println(age);
Field address = clazz.getDeclaredField("address");
address.setAccessible(true);
System.out.println(address);
System.out.println("------------------------");
//3、通过反射获取所有变量,包括private、protected、default。
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//4、获取成员变量名字
System.out.println(field.getName());
//5、获取权限修饰:0是default、1是public、2是private、4是protected。或者使用Modfier.toString(modifiers)输出
int modifiers = field.getModifiers();
System.out.println(modifiers);
//6、获取成员变量数据类型
Class<?> type = field.getType();
System.out.println(type);
System.out.println("---------------");
}
}
}
5、获取方法并且调用
获取方法用到的是Method类,内部方法也是一些getXXX()和setXXX的方法,这些方法就不演示了,和上面获取Field大同小异,可以参考上面的例子。然后我们主要来演示一些与上面不一样的方法。
为了测试获取方法上面的Annotation方法,我创建了一个MyAnnotation注解,然后让其作用在Person类的display1()方法上面。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String[] value() default "tang_hao";
}
需要注意的是@Retention必须要设置为RUNTIME类型,否则是获取不到的。
public class Reflection {
public static void main(String[] args) throws Exception {
//获取Person的Class对象
Class<?> clazz = Class.forName("com.thr.Person");
//1、通过反射获取所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
//2、获取方法的名称
System.out.println("名称:"+method.getName());
//3、获取方法上面的注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("注解:"+annotation);
}
//4、获取方法返回值类型
Class<?> returnType = method.getReturnType();
System.out.println("返回值类型:"+returnType);
//5、获取形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
if (!(parameterTypes==null||parameterTypes.length==0)){
for (Class<?> parameterType : parameterTypes) {
System.out.println("形参列表:"+parameterType.getName());
}
}
System.out.println("----------------");
}
}
}
如果我们还想要使用反射来调用方法,那么就需要使用到这个方法:
- invoke(Object obj, Object... args):传递object对象及参数然后调用该对象所对应的方法
public class Reflection {
public static void main(String[] args) throws Exception {
//获取Person的Class对象
Class<?> clazz = Class.forName("com.thr.Person");
Object o = clazz.newInstance();
Method display1 = clazz.getDeclaredMethod("display1");
display1.invoke(o);
Method display2 = clazz.getDeclaredMethod("display2");
display2.setAccessible(true);
display2.invoke(o);
}
}
6、获取构造器并且创建实例
获取方法用到的是Constructor类,构造器的获取比较的简单,简单演示一下:
public class Reflection {
public static void main(String[] args) throws Exception {
//获取Person的Class对象
Class<?> clazz = Class.forName("com.thr.Person");
//1、获取public的构造器
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("-----------------------");
//2、获取所有的构造器
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("-----------------------");
//3、使用反射创建实例,默认构造器
Constructor<?> constructor = clazz.getConstructor();
System.out.println(constructor.getName());
Object o = constructor.newInstance();
Person p= (Person) o;
System.out.println(p.toString());
//有参构造器
Constructor<?> constructor1 = clazz.getConstructor(String.class, int.class,String.class);
Object o1 = constructor1.newInstance("tang_hao",20,"China");
Person p1= (Person) o1;
System.out.println(p1.toString());
}
}
其实后面还有获取父类、泛型、接口、所在包等等其他的就不多说了,其实都是大同小异,理解了上面这些,后面这些应该也会了。
7、小结
从前面学到这里我们大概对反射也应该有一个简单的了解了,利用反射可以获取成员变量、方法、构造器、注解等属性,甚至是私有的方法,这在普通创建实例的方式是不可能的。虽然我们在平时写代码很少用到反射技术,但是我们要知道后面会学习框架知识,而框架的灵魂就是反射,所以学好反射基础对我们来说是非常有必要的。
夯实Java基础(二十一)——Java反射机制的更多相关文章
- Java基础(十一)——反射
一.概述 1.介绍 Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法. 加载完类 ...
- Java基础系列 - 泛型和反射机制
package com.test5; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * Java泛型和反射机 ...
- Java进阶(二十一)java 空字符串与null区别
java 空字符串与null区别 1.类型 null表示的是一个对象的值,而并不是一个字符串.例如声明一个对象的引用,String a = null ; ""表示的是一个空字符串, ...
- 【夯实PHP基础】PHP的反射机制
本文地址 分享提纲: 1. 介绍 2. 具体例子 2.1 创建Persion类 2.2 反射过程 2.3 反射后使用 1. 介绍 -- PHP5添加了一项新的功能:Reflection.这个功能使得p ...
- Java开发培训基础知识解析之反射机制
Java是老牌编程语言,是当前应用最广泛的编程语言之一.想要学习Java你就一定要掌握Java基础知识,而反射对于初学Java的人来说绝对是非常重要的知识点.什么是反射?如何理解反射机制?如何使用反射 ...
- Java面试题总结之Java基础(二)
Java面试题总结之Java基础(二) 1.写clone()方法时,通常都有一行代码,是什么? 答:super.clone(),他负责产生正确大小的空间,并逐位复制. 2.GC 是什么? 为什么要有G ...
- Java入土--Java基础(二)
Java基础(二) 接上一讲,我们接着来聊聊Java的一些基础知识,下一讲就会进行流程的控制. 类型转换 首先呢,是类型的转换,接上一个内容的数据类型,类型转换就是数据类型更进一步的应用. 由于Jav ...
- 062 01 Android 零基础入门 01 Java基础语法 07 Java二维数组 01 二维数组应用
062 01 Android 零基础入门 01 Java基础语法 07 Java二维数组 01 二维数组应用 本文知识点:二维数组应用 二维数组的声明和创建 ? 出现空指针异常 数组的名字指向数组的第 ...
- 010 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 04 变量的三个元素的详细介绍之二——变量类型——即Java中的数据类型
010 01 Android 零基础入门 01 Java基础语法 02 Java常量与变量 04 变量的三个元素的详细介绍之二--变量类型--即Java中的数据类型 Java中变量的三要素 变量名 变 ...
- Java基础16:Java多线程基础最全总结
Java基础16:Java多线程基础最全总结 Java中的线程 Java之父对线程的定义是: 线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(比如文件句柄等)也能访问同一个进 ...
随机推荐
- zabbix-server报错:No route to host
前戏: 我在VM虚拟机中创建了2个liunx系统(rhel7和cent7),一个用作zabbix服务端,另一个用作zabbix客户端.但是用服务端监控客户端时图标是红色的监控不了,报错信息为:Get ...
- 最全的 eclipse web 项目目录结构以及Tomcat的各个目录的作用
本文会尽可能对 java web 项目的目录结构做出最详细的解释,尽量做到浅显易懂. eclipse web 项目目录结构 java web 项目事例 03-springmvc-drien-xgq ...
- Python学习之列表篇
浮点数类型:round(x,d)可对浮点数进行四舍五入,科学计数法:aeb表示a*10^bpython大小写敏感整数类型:无范围限制,pow(x,y)表示x^y,想算多大算多大,四种表示形式:十进制, ...
- List 重载添加-add,删除-remove方法,以及获取子集方法
package seday12; import java.util.ArrayList;import java.util.List; /*** @author xingsir* List重载了一对ad ...
- 201771010135 杨蓉庆《面对对象程序设计(java)》第十八周学习总结
1.实验目的与要求 (1) 综合掌握java基本程序结构: (2) 综合掌握java面向对象程序设计特点: (3) 综合掌握java GUI 程序设计结构: (4) 综合掌握java多线程编程模型: ...
- B站上线互动视频背后,是一场谁都输不起的未来之战
毋庸置疑的是,视频网站的竞争已愈发激烈.而它们的竞争体现在多个维度,比如买视频会员赠送购物网站会员.依靠各自的社交体系不断尝试打破圈层瓶颈等.当然,最直接的竞争还是体现在内容层面.购买独家版权.制作原 ...
- ASP.NET Core搭建多层网站架构【8.1-使用ViewModel注解验证】
2020/01/29, ASP.NET Core 3.1, VS2019 摘要:基于ASP.NET Core 3.1 WebApi搭建后端多层网站架构[8.1-使用ViewModel注解验证] 使用V ...
- 序列变换 HDU - 5256
序列变换 HDU - 5256 题目链接 题目 我们有一个数列A1,A2...An,你现在要求修改数量最少的元素,使得这个数列严格递增.其中无论是修改前还是修改后,每个元素都必须是整数. 请输出最少需 ...
- 【代码审计】VAuditDemo 命令注入漏洞
一般PHP中可以使用下列函数来执行外部的应用程序或命令 system() exec() passthru() shell_exec() 跟踪$cmd --> 跟进$target,发现传递给tar ...
- ubuntu mysql允许root用户远程登录
有两种方法: 一. 1.mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPT ...