反射概述

问题:我们平时书写在idea中的Java程序是如何运行的呢?

1)首先将 .java 源文件编译为class类文件;

2)编译后的类文件是存在硬盘中的,那么我们运行需要在内存中看到效果,那么类文件是如何被加载到内存中的呢,就是jvm通过类加载器ClassLoader把硬盘中的class文件加载到内存中,这样就可以使用这个类中的成员变量和方法了。而被加载到内存中这个class文件就会变成一个Class类的对象。

反射要依赖于Class类。

由于Class表示类文件的字节码文件对象,类字节码文件就是在描述一个类,描述类的成员变量、成员函数和构造函数。

而反射就是从一个类的字节码文件中拿到成员变量、成员函数和构造函数。要想从一个类中拿东西必须拿到这个类的字节码文件对象,所以反射依赖于Class,因此我们在学习反射之前先了解下Class。

Class类介绍

在Java中使用类来描述所有的事物,而这些描述完的所有程序,在编译完之后统一都会生成各自class文件。

在Java中class文件是所有源代码(.java 文件)程序编译后统一的结果。class文件是一类可以被JVM直接执行的文件。class文件在Java世界中就是存在的一类事物。

Java使用Class类来描述和封装class文件这类事物。class文件也叫作字节码文件。

关于Class描述字节码文件如下图所示:

说明:

1)Java中使用Class类表示某个class文件;

2)任何一个class文件都是Class这个类的一个实例对象;

Class的API描述:

说明:

1)Class类它可以表示Java中的任何内容;

2)Java中使用Class类表示硬盘上的某个.class文件,启动JVM就会把文件加载到内存中,占用一片空间,称为一个字节码文件对象,这个对象就是Class类的一个实例。不同的类,有自己的字节码文件对象,这些对象都是Class类的实例;

3)说明:在Class类中专门提供了几个获取成员变量 、成员函数 、构造函数的函数。

FieldgetField(String name) 表示获取类中的成员变量的对象;

MethodgetMethod() 表示获取类中的成员函数的对象;

ConstructorgetConstructor() 表示获取类中的构造函数的对象;

获取Class的三种方式(掌握)

因为反射技术是通过Class对象来实现把一个类进行解剖的,所以需要先了解怎么样才可以获取到Class对象。

需求:演示获取Class的三种方式:

1)获取Class对象的第一种方式:使用类的class属性直接获取:类名.class。

说明:在任何的一个类中都有一个静态成员变量class,可以直接获取到class文件所属的Class对象。

2)获取Class对象的第二种方式:在Object类中,有个getClass方法,就可以获取到任何一个对象对应的Class对象。对象.getClass()。

3)获取Class对象的第三种方式:在Class类中有个静态的方法:static Class forName(String className),根据类的名称获取类的Class对象。

说明:这里的参数className必须是类的全名(就是带有包名的全名称)。如:Class.forName("java.lang.String");

补充:上述三种方式可以获得Class对象,获得完Class对象就可以获取类的基本信息:

获取类的基本信息:

String getName()获取类的名称,就是获得包名.类名

StringgetSimpleName() 获取类的简称 类名

代码演示如下所示:

/*

* 演示:获取Class的三种方式

* 方式1:类名.class

* 方式2:对象.getClass()

* 方式3:static ClassforName(String className)根据类的名称获取类的Class对象

*      注意:这里的className必须是类的全名!

*

* 获取类的基本信息:

*  String getName() 获取类的名称 包名.类名

*  StringgetSimpleName() 获取类的简称  类名

*/

public class ClassDemo1 {

public static void main(String[] args) throws ClassNotFoundException {

// 方式1:类名.class

Class clazz = String.class;

// 输出clazz全名 就是包名.类名java.lang.String

System.out.println(clazz.getName());

// 输出clazz精简名字 就是类名 String

System.out.println(clazz.getSimpleName());

// 方式2:对象.getClass()

Class clazz2 = "柳岩".getClass();

// 输出clazz2全名 就是包名.类名java.lang.String

System.out.println(clazz2.getName());

// 输出clazz2精简名字 就是类名 String

System.out.println(clazz2.getSimpleName());

// 方式3:static ClassforName(String className)根据类的名称获取类的Class对象

//      Class clazz3 =Class.forName("String");//这里必须是类的全名 包名.类名,否则报java.lang.ClassNotFoundException

Class clazz3 = Class.forName("java.lang.String");

// 输出clazz3全名 就是包名.类名java.lang.String

System.out.println(clazz3.getName());

// 输出clazz3精简名字 就是类名 String

System.out.println(clazz3.getSimpleName());

//说明对于String类来说在内存中只有一个String.class文件

System.out.println(clazz==clazz2);//true

System.out.println(clazz==clazz3);//true

System.out.println(clazz2==clazz3);//true

}

}

总结:上述三种方式都是用来获取Class对象的,那么在开发中一般使用哪种获取方式呢?

在开发中我们会使用第三种获取方式。

说明:第三种的好处就是加载一个类却不需要知道这个类是什么,通过第一种方式获取前提是必须得先知道类名,然后才能通过类名.class获取。而第二种方式必须知道对象才能通过对象.getClass()来获取。

而第三种不需要知道类名或者对象就可以直接获取,或者可以这样理解,我们在真实开发中,类的名字是不知道的,都是通过IO流来读取配置文件读取回来的。

也就是说我们读取配置文件的时候根据key来获取String类型的value,这样就可以把String类型的value作为forName(StringclassName)函数的参数,而不需要在程序代码中体现出类名字样就可以获得Class对象了。

预定义对象(了解)

通过查阅Class的API发现8种基本数据类型、void关键字和数组也表示为Class的对象,我们把8种基本数据类型、void叫做预定义对象,一共有9种。

说明:

1)基本数据类型要保存在内存中,那么他们也会有自己的字节码,获取的Class对象中保存的就是他们本身,因为基本数据类型不属于任何包下的。

如:Class c = int.class;对象c中保存的就是int。

2)对于函数返回值类型void,代表的是没有返回值类型,那么他也是一种数据类型,表示没有类型,那么输出对象也是void本身。

如:Class c1 = void.class;对象c1中保存的就是void。

代码演示如下:

/*

* 演示:9种预定义对象:

* 包含:8种基本数据类型以及void

*/

public class ClassDemo2 {

public static void main(String[] args) {

// 获取int类型的Class对象

Class c = int.class;

System.out.println(c);// int

// 获取void类型的Class对象

Class c1 = void.class;

System.out.println(c1);// void

}

}

总结:任何的一种数据都具有自己的字节码。

反射的概念

到目前为止我们已经了解了如何获得Class,那么接下来我们就会使用反射技术来剖析一个class文件。

那么到底什么是反射呢?

反射就是通过一个类的Class对象把类中的各种成员映射成对应的Java类。一个类中的:成员变量、构造函数、成员方法都有对应的Java类:Field、Contructor、Method; 就比如:一个汽车是一个大类,汽车中的发动机、轮胎等等都可以是一个个小的类。

一个类的Class对象可以获取其所有成员的信息,比如一个方法的名称、修饰符、参数类型、返回值等等信息封装成一个描述方法的类(Method)中。

换句话说反射通过Class类的对象可以获取一个类中的成员,比如函数,保存在Method类中。然后通过Method类的对象来获取一个成员函数的名称、修饰符、参数类型、返回值等等信息。

一个类中的所有成员,都可以通过Class对象获得,并封装为对应的对象。我们拿到这些对象以后,有什么用?怎么用?这正是我们学习反射的要点!

使用反射获取一个类中的构造函数

使用Class类中的newInstance()函数创建某个类的对象

通过上述办法已经获取到了Class对象,即就是获取到某个类的class文件。现在我们不使用Java中的new关键字来创建这个class文件所描述的那个事物的真实对象, 而通过Class这个对象,动态的创建这个类的真实对象。

​ 在Class类中有一个newInstance()方法,可以动态的创建出当前这个class文件的真实对象。

该方法如下所示:

代码演示如下所示:

Person类:

/*

* 描述人

*/

public class Person {

//属性

private String name;

private int age;

public Person(String name, int age) {

this.name = name;

this.age = age;

}

public Person() {

}

}

测试类:

/*

* 使用Class类中的newInstance()函数获取某个class文件中的真实对象

*/

public class ReflectDemo1 {

public static void main(String[] args) throws Exception {

// 获取Class类的对象

Class clazz = Class.forName("cn.itcast.sh.reflect.demo.Person");

//使用对象调用函数创建Person类的对象

Person p = (Person) clazz.newInstance();

/*

* 上述两行代码等同于

*Person p= new Person();

*注意:这里是使用Person类中的无参构造函数在创建对象,所以要求Person类中必须具备无参构造函数,

*否则就会报java.lang.InstantiationException:不能实例化异常

*/

System.out.println(p);//cn.itcast.sh.reflect.demo.Person@5a20d10a

//利用无参构造函数创建对象

String s = String.class.newInstance();

System.out.println(s.length());//0

}

}

注意:使用Class类中的newInstance()方法的时候,要求class文件中必须有空参数并且不能是private修饰的构造函数。如果没有,那么使用newInstance()方法就会报异常。

使用反射获取一个类中的所有构造函数(包括有参数和私有的构造函数)(Constructor类)

说明:

1)使用Class类的对象即字节码对象可以获取class文件中的所有构造函数,具体应该借助Class类中的如下函数:

ConstructorgetDeclaredConstructor(Class ... parameterTypes)根据参数列表获取指定的构造函数 包括私有的

说明:由于这里需要Class类的对象,所以在给参数的时候,直接使用实参类型的类获取Class对象即可。

举例:假设需要获取String类型的构造函数,那么这里直接使用String.class作为getDeclaredConstructor(Class ... parameterTypes)的参数。

2)获取到构造函数对象之后,就可以使用获取的构造函数对象创建某个类的真实对象。我们通过反射已经获取到构造函数,查阅Constructor类中的描述,发现Constructor类中的newInstance(Object...initargs) 方法,这个方法可以动态的创建出这个构造函数对象所表示的那个类的真实对象。

​ 说明: Object... initargs 创建对象的时候需要传递的真实的数据,就是构造函数所需要的实际参数。

代码如下所示:

需求:获取File类的构造函数,并使用获取的构造函数创建对象。

D:\test\out.txt 作为newInstance函数的参数,创建对象之后并获取绝对路径。

/*

* 演示:反射获取构造函数。

*/

public class ReflectDemo2 {

public static void main(String[] args) throws Exception {

// 获取字节码文件对象

Class clazz=File.class;

//需求:获取public File(String s)

//ConstructorgetDeclaredConstructor(Class ... parameterTypes)根据参数列表获取指定的构造函数 包括私有的

System.out.println("--------------");

//publicFile(String s)

Constructor constructor2 = clazz.getDeclaredConstructor(String.class);

System.out.println(constructor2);

//使用获得的构造函数创建对象 newInstance(Object... initargs)

File f = (File) constructor.newInstance("D:\\test\\out.txt");

System.out.println(f.getAbsolutePath());//D:\test\out.txt

}

}

注意:通过Class类中的Constructor getDeclaredConstructor(Class... parameterTypes)函数可以获得类中的所有的构造函数,包括私有的构造函数,但是私有的构造函数我们在其他类中是无法使用的,如果要想使用必须强制取消Java对私有成员的权限检测或者可以理解暴力访问。

需求:使用反射技术获得File类中的私有构造函数 privateFile(String child, File parent) 并创建File类的对象获得指定路径的绝对路径。

注:String child="柳岩.jpg";Fileparent=new File("D:\test");

代码实现如下所示:

public static void main(String[] args) throws Exception {

// 获取字节码文件对象

Class clazz=File.class;

/*

*  需求:使用反射技术获得File类中的私有构造函数   privateFile(String child, File parent)

*  并创建File类的对象获得指定路径的绝对路径。

*  注:String child="柳岩.jpg",File parent=newFile("D:\\test")

*/

Constructor constructor2 = clazz.getDeclaredConstructor(String.class,File.class);

//创建File类的对象 使用获得的构造函数创建对象 newInstance(Object... initargs)

File f2 = (File) constructor2.newInstance("柳岩.jpg",new File("D:\\test"));

System.out.println(f2.getAbsolutePath());

}

上述代码发生了如下异常:

IllegalAccesssException异常是在没有访问权限时,就会引该异常。

解决上述异常,我们必须强制取消Java对私有成员的权限检测或者可以理解暴力访问,需要使用Constructor的父类AccessibleObject类中的函数:

AccessibleObject类如下所示:

使用如下函数即可:

上述函数表示强制取消Java对私有成员的权限检测。

或者可以理解暴力对私有成员进行访问。

改进的代码如下所示:

/*

*  需求:使用反射技术获得File类中的私有构造函数   privateFile(String child, File parent)

*  并创建File类的对象获得指定路径的绝对路径。

*  注:String child="柳岩.jpg",File parent=newFile("D:\\test")

*/

Constructor constructor2 = clazz.getDeclaredConstructor(String.class,File.class);

//对于上述的构造函数是私有的,我们不能直接访问,只能暴力访问。

constructor2.setAccessible(true);

//创建File类的对象 使用获得的构造函数创建对象 newInstance(Object... initargs)

File f2 = (File) constructor2.newInstance("柳岩.jpg",new File("D:\\test"));

System.out.println(f2.getAbsolutePath());//D:\test\柳岩.jpg

小结:

当要访问Class对象中的私有的构造或成员时,需要使用getDeclaredXxxx()函数:

Xxxx表示:Constructor、Field、Method。

在访问Class对象中的私有的构造函数或成员时,需要取消java语言的默认访问权限检查

setAccessible(boolean)true表示强制取消Java对私有成员的权限检测。 false表示不会取消Java对私有成员的权限检测。

反射获取成员方法(Method)(掌握)

反射公开的非静态的成员方法

MethodgetDeclaredMethod(String name,Class ... parameterTypes)获取某个方法。

说明:

1)在Class类中提供的getDeclaredMethod方法上接收一个String name,name表示的是需要反射的那个方法的名字。

​ 因为在一个类中可以有多个不同名的方法。在反射的时候需要指定这个方法的名字,同时在一个类中还可能出现方法的重载,这时还需要指定具体反射的是哪个方法参数类型。

2)让反射到的一个方法运行,需要使用Method类中的invoke方法 :

Objectinvoke(Object obj, Object... args)

​ invoke方法中的第一个参数 Object obj:表示的是当前需要调用这个方法的那个对象

​ invoke方法中的第二个参数Object... args:

​ 表示的是真正需要运行的某个类中被反射的那个方法需要接收的真实参数

​ 在调用Method类中的invoke方法的时候,其实底层是在运行被反射的那个方法,

​ 既然是某个方法在运行,那么方法运行完之后可能会有返回值。

举例:需求:我们想通过反射技术获得Person类中的setName()函数,并让其执行。

public class ReflectDemo {

public static void main(String[] args) throws Exception {

//获取Class对象

Class clazz = Class.forName("cn.itcast.sh.reflect.demo.Person");

/*

* 反射成员方法:

* public void setName(String name)

* 类中的非静态的成员方法,需要对象调用,我们反射到方法之后,最后肯定是要运行这个方法

* 这时肯定还是需要对象的

*

* Method getDeclaredMethod(String name,Class>... parameterTypes)

* String name 反射的方法的名字

* Class>... parameterTypes 反射的方法接受的参数类型

*/

Method method = clazz.getDeclaredMethod("setName", String.class);

Person p = new Person();

//通过非反射的方式执行setName函数

/*

* Person p = new Person();

*  p.setName("赵四");

*  System.out.println(p.getName());//赵四

*/

//通过反射的方式执行setName函数

/*

* 让反射到的一个方法运行,需要使用Method类中的invoke方法

*

* Object invoke(Object obj, Object... args)

*

* invoke方法中的第一个参数 Object obj:表示的是当前需要调用这个方法的那个对象

* invoke方法中的第二个参数Object... args:

*          表示的是真正需要运行的某个类中被反射的那个方法需要接收的真实参数

*  在调用Method类中的invoke方法的时候,其实底层是在运行被反射的那个方法,

*  既然是某个方法在运行,那么方法运行完之后可能会有返回值

*/

//执行setName函数   这句代码就是在调用反射到Person类中的setName方法

Object obj2 = method.invoke(p, "赵四");

System.out.println(p);//cn.itcast.sh.reflect.demo.Person@7f21c5df

System.out.println(obj2);//null

System.out.println(p.getName());//赵四

System.out.println("=====================================");

}

}

总结:

反射过程如下图所示:

更多免费技术资料可关注:annalin1203

java j_Javaj基础之反射相关推荐

  1. JAVA重要基础之反射

    什么是反射? 答:在JVM运行阶段,能够动态地获取信息(字节码实例.构造器.方法.字段),动态地进行对象的创建,方法执行,字段操作. 反射常用的类: (1)Class:所有类的字节码实例描述 (2)C ...

  2. 反射 字段_java核心基础之反射

    前言 大家好,我是 jack xu,今天跟大家介绍核心基础里面的反射,反射这个东西你说它重要也重要,不重要也不重要.重要是当你看一些框架的源码时候,里面会用到反射的代码,你不会是看不懂的.不重要是因为 ...

  3. Java、Android基础之—反射

    前言 写这篇文章原因之一,是因为之后会对一些Android第三方框架进行分析.虽不能达到完全清晰,但大体的涉及知识需明白. 反射概述 反射是框架设计的灵魂.(使用的前提条件:必须先得到代表的字节码的C ...

  4. JAVA常用基础知识点[继承,抽象,接口,静态,枚举,反射,泛型,多线程...]

    类的继承 Java只支持单继承,不允许多重继承 - 一个子类只能有一个父类 - 一个父类可以派生出多个子类 这里写图片描述 子类继承了父类,就继承了父类的方法和属性. 在子类中,可以使用父类中定义的方 ...

  5. java 反射泛型方法_java基础之反射和泛型以及注解

    java基础之反射和泛型以及注解 泛型擦除 泛型擦除: 泛型只在编译时期有效,编译后的字节码文件中不存在泛型信息. 声明泛型集合,集合两端类型必须一致.类型也可以用包装类型,泛型的类型必须是引用类型, ...

  6. JAVA常用基础知识点[继承,抽象,接口,静态,枚举,反射,泛型,多线程.]

    类的继承 Java只支持单继承,不允许多重继承  - 一个子类只能有一个父类  - 一个父类可以派生出多个子类    子类继承了父类,就继承了父类的方法和属性.  在子类中,可以使用父类中定义的方法和 ...

  7. Java 语言基础(异常机制和File类,IO流,多线程,网络编程,反射机制)

    原文:Java 语言基础(异常机制和File类,IO流,多线程,网络编程,反射机制) 异常机制和File类 异常机制 基本概念 异常就是"不正常"的含义,在 Java 语言中主要指 ...

  8. Java基础之—反射(非常重要)

    反射是框架设计的灵魂 (使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)) 一.反射的概述 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道 ...

  9. java反射头文件_编程基础知识——C++能不能支持Java和ObjC的反射?

    C++能不能支持Java和ObjC的反射? 要回答这个问题.首先我们要清楚什么是反射.什么是反射? 教科书的解释我就不说了,(^o^)事实上我也记不得.实际开发应用的反射就是在没有某个类型的头文件或者 ...

最新文章

  1. 2022 AAAI Fellow放榜!SAIL首席科学家颜水成、港科大教授冯雁等10人入选
  2. 编写Dockerfile文件,构建自己的centos镜像
  3. 增加数据_太原二手房七月数据出炉,挂牌量增加800余套,万柏林区涨幅大
  4. 什么是 Java 对象深拷贝?面试必问!
  5. MongoDB数据库--扩展Base64,算法
  6. 舞动的桥 阿里云首个百万IOPS云盘的背后
  7. 【甘道夫】HBase基本数据操作的详细说明【完整版,精绝】
  8. 腾讯回应两块八卖《鬼谷八荒》:已下架并退款;iMac Pro 售完即停产;iCloud 识别姓氏闹乌龙 | 极客头条...
  9. 这本在日本销量突破70W+的 Python 书,凭什么这么火?
  10. 金三银四大厂面经总结,java模块化打包
  11. 小菜找实习-动态规划
  12. 2020年中国科技核心期刊目录(自然科学卷)
  13. 计算机学课毕业论文,计算机专业毕业论文(精选5篇)
  14. js 计算时间差 函数
  15. linux解压文件并命名,linux命名压缩解压文件
  16. RestTemplate的No instances available for xxx
  17. python计算一个三位数个位数之和
  18. 真百式 - 饺子的做法
  19. PAT难题精选题汇总,代码保存下来以供自己学习
  20. GetKeyState函数详解

热门文章

  1. 人工智能如何改变我们的未来生活
  2. Responder Chain简析
  3. ecology报错 License文件上传出错!
  4. 深入浅出JBoss Seam
  5. QQ群消息的屏蔽与重要消息自动提取
  6. linux屏幕亮度调节 fn,51CTO博客-专业IT技术博客创作平台-技术成就梦想
  7. rtc芯片测试软件,希望有做过stm32下RTC芯片R8025 AC的朋友看看
  8. 查询企业电话号码有哪些平台?怎样查找企业联系方式
  9. 系统光盘内容怎么考到计算机里,怎么把光盘里的东西复制到电脑
  10. 【爬虫+MongoDB】《星际穿越》豆瓣影评信息爬取