java反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
Java反射机制主要提供了以下功能:在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法
反射与正常创建的区别:
- 正常创建对象的方法(使用
new
关键字)在编译时就已经知道要创建哪个类的实例。 - 反射创建对象的方法(使用
Class.forName()
和newInstance()
)在编译时不知道要创建哪个类的实例,而是在运行时动态指定。
1. 获取包名
方式一:利用Object类的getClass()方法,但是要求必须先产生指定类的对象才可以,几乎不用。
import java.util.Date;
public class ReflectionTest {
public static void main(String[] args) {
Date date = new Date(); //实例化一个date对象
Class<?> cls = date.getClass();
System.out.println(cls); //输出类的包名
}
}
方式二:利用“类.class”的形式取得Class类的对象
类.class
语法在 Java 中用于获取类的 Class
对象,并且它属于类加载机制的一部分。与 Class.forName
方法类似,类.class
语法也是获取 Class
对象的一种方式,但它的行为有所不同。
类.class
与类加载
- 编译时常量:
类.class
语法在编译时解析,不会触发类的初始化。它仅仅是获取Class
对象的引用,不会导致类加载过程(加载、链接和初始化)中的任何阶段被执行。 - 直接引用:使用
类.class
语法直接引用类的Class
对象,这个引用在编译期就确定了。
public class ReflectionTest {
public static void main(String[] args) {
Class<?>cls = java.util.Date.class;
System.out.println(cls);
}
}
方式三:利用Class类提供的一个方法(forName)完成,在系统架构中使用
Class.forName
方法会根据提供的类名查找并加载类,返回对应的 Class
对象。这个过程会触发类加载过程,包括类的加载、链接和初始化。具体来说,Class.forName
执行的步骤如下:
- 类加载:将类的字节码从文件或其他来源加载到 JVM 中。
- 类链接:
- 验证:验证类的字节码是否符合 JVM 的规范。
- 准备:为类的静态变量分配内存,并初始化为默认值。
- 解析:将符号引用替换为直接引用。
- 类初始化:执行类的静态初始化块和静态变量的赋值操作。
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException {
Class<?>cls = Class.forName("java.util.Date");
System.out.println(cls);
}
}
方式四:利用Class类提供的一个方法(ClassLoader)完成,在系统架构中使用
ClassLoader.getSystemClassLoader().loadClass("java.util.Date")
是使用系统类加载器加载指定类的方法。这种方式与 Class.forName
和 类.class
语法有所不同,它提供了更细粒度的控制,并且允许你使用特定的类加载器来加载类。下面详细描述这种方法的行为及其与其他方法的区别。
使用 ClassLoader.loadClass
方法加载类
ClassLoader.loadClass
方法用于通过指定的类加载器加载类,它会执行类加载和链接,但默认情况下不会初始化类。这与 Class.forName
方法不同,后者默认情况下会触发类的初始化。
public class ReflectionTest02 {
public static void main(String[] args) throws ClassNotFoundException {
Class date = ClassLoader.getSystemClassLoader().loadClass("java.util.Date");
System.out.println(date);
}
}
2. 对象实例化
实例化对象可以有两种形式,一种是通过关键字new,另外一种是通过反射机制完成
关键词new
import java.util.Date;
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Date date = new Date(); //实例化一个date对象
System.out.println(date);
}
}
通过反射获取包名后实例化(无参数)
import java.util.Date;
public class ReflectionTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?>cls = Class.forName("java.util.Date");
Date date = (Date)cls.newInstance();
System.out.println(date);
}
}
通过反射获取包名后实例化(有参数)
2.1.获取构造方法
public class Book {
public Book(String name){
System.out.println("我的名字是"+"name");
}
public Book(String name,double price){
System.out.println("我的名字是"+name+",我的价格是"+price);
}
private Book(String name,double price,String author){
System.out.println("我的名字是"+name+",我的价格是"+price+",我是被"+author+"创造出来的");
}
}
获取公有的构造方法
import java.lang.reflect.Constructor;
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("Book");
//进行实例化
Constructor[] conArr = cls.getConstructors();
for (Constructor c : conArr){
System.out.println(c);
}
}
}
获取公有+私有的构造方法
import java.lang.reflect.Constructor;
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("Book");
//进行实例化
Constructor[] conArr = cls.getDeclaredConstructors();
for (Constructor c : conArr){
System.out.println(c);
}
}
}
2.2通过构造方法实例化对象
class Book {
private String title; //创建title属性,类型为私有
private double price; //创建price属性,类型为私有
public Book(String title,double price){
this.title = title;
this.price = price;
}
public void setTitle(String title) {
this.title = title;
}
public void setPrice(double price) {
this.price = price;
}
//重写String方法
@Override
public String toString() {
return "图书名称" +this.title + "图书价格"+this.price;
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class BookTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> cls = Class.forName("Book");
Constructor<?> cons = cls.getConstructor(String.class,double.class);
Book book = (Book)cons.newInstance("JAVA反射学习",79.8);
System.out.println(book);
}
}
3. 获取public方法与属性
3.1 获取public方法
- 新建一个Book类,并且有以下几个方法
public class Book {
private String title; //创建title属性,类型为私有
private double price; //创建price属性,类型为私有
public void setTitle(String title) {
this.title = title;
}
public void setPrice(double price) {
this.price = price;
}
public void appraise(){
System.out.println("我的书名是"+this.title+",我十分好看,并且物美价廉,只要"+this.price);
}
}
- 使用反射获取方法
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BookTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> cls = Class.forName("Book");
//进行实例化
Book book = (Book)cls.newInstance();
//获取到setPrice方法
Method setPriceMesthod = cls.getMethod("setPrice", double.class);
//将方法与对象绑定,并传入对应的值
setPriceMesthod.invoke(book,19.56);
Method setTitleMethod = cls.getMethod("setTitle", String.class);
setTitleMethod.invoke(book,"JAVA反射");
Method appraiseMethod = cls.getMethod("appraise");
appraiseMethod.invoke(book);
}
}
3.2 获取public属性
- 新建一个Book类,并且有以下两个属性
public class Book {
public String title; //创建title属性,类型为私有
public double price; //创建price属性,类型为私有
public void appraise(){
System.out.println("我的书名是"+this.title+",我十分好看,并且物美价廉,只要"+this.price);
}
}
- 获取方法并传入值
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("Book");
//进行实例化
Book book = (Book)cls.newInstance();
//获取到属性并为其赋值,并与实例化出来的对象绑定
Field title = cls.getDeclaredField("title");
title.set(book,"《java反射》");
Field price =cls.getDeclaredField("price");
price.set(book,9.9);
book.appraise();
}
}
4. 获取private方法与属性
4.1 获取private方法
- 新建一个Book类,并且有以下几个方法
public class Book {
private String title; //创建title属性,类型为私有
private double price; //创建price属性,类型为私有
private void setTitle(String title) {
this.title = title;
}
private void setPrice(double price) {
this.price = price;
}
private void appraise(){
System.out.println("我的书名是"+this.title+",我十分好看,并且物美价廉,只要"+this.price);
}
}
- 获取private方法,获取方法以后,需要.setAccessible(true)setAccessible:阻止java对修饰符的检查
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("Book");
//进行实例化
Book book = (Book)cls.newInstance();
//获取到setPrice方法
Method setPriceMesthod = cls.getDeclaredMethod("setPrice", double.class);
//设置setAccessible为true
setPriceMesthod.setAccessible(true);
//将方法与对象绑定,并传入对应的值
setPriceMesthod.invoke(book,19.56);
Method setTitleMethod = cls.getDeclaredMethod("setTitle", String.class);
setTitleMethod.setAccessible(true);
setTitleMethod.invoke(book,"JAVA反射");
Method appraiseMethod = cls.getDeclaredMethod("appraise");
appraiseMethod.setAccessible(true);
appraiseMethod.invoke(book);
}
}
4.2 获取private属性
public class Book {
private String title; //创建title属性,类型为私有
private double price; //创建price属性,类型为私有
}
import java.lang.reflect.Field;
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("Book");
//进行实例化
Book book = (Book)cls.newInstance();
//获取到属性并为其赋值,并与实例化出来的对象绑定
Field title = cls.getDeclaredField("title");
title.setAccessible(true);
title.set(book,"《java反射》");
Field price =cls.getDeclaredField("price");
price.setAccessible(true);
price.set(book,199.9);
System.out.println("我的名字是"+title.get(book)+",我的价格是"+price.get(book));
}
}
4.2.2 获取final修饰的private属性,并修改
public class Book {
private String title; //创建title属性,类型为私有
final private double price=9.9; //创建price属性,类型为私有
}
import java.lang.reflect.Field;
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("Book");
//进行实例化
Book book = (Book)cls.newInstance();
//获取到属性并为其赋值,并与实例化出来的对象绑定
Field title = cls.getDeclaredField("title");
title.setAccessible(true);
title.set(book,"《java反射》");
Field price =cls.getDeclaredField("price");
price.setAccessible(true);
price.set(book,199.9);
System.out.println("我的名字是"+title.get(book)+",我的价格是"+price.get(book));
}
}
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("Book");
//进行实例化
Book book = (Book)cls.newInstance();
//获取到属性并为其赋值,并与实例化出来的对象绑定
Field title = cls.getDeclaredField("title");
title.setAccessible(true);
title.set(book,"《java反射》");
Field price =cls.getDeclaredField("price");
//删除final
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(price, price.getModifiers() & ~Modifier.FINAL);
price.setAccessible(true);
price.set(book,199.9);
System.out.println("我的名字是"+title.get(book)+",我的价格是"+price.get(book));
}
}
5. 获取所在包是否执行静态代码块
public class Book {
// 静态变量
private static int bookCount;
// 静态代码块,类加载时执行
static {
bookCount = 0;
System.out.println("执行静态代码块,初始化bookCount为:" + bookCount);
}
}
执行
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> cls= Class.forName("Book");//执行
}
}
不执行
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?>cls1 = Book.class; //没执行
}
}
不执行
public class BookTest {
public static void main(String[] args) throws Exception {
Class book = ClassLoader.getSystemClassLoader().loadClass("Book");//没执行
}
}
6. 获取内部类
public class Book {
public void outMethod(){
System.out.println("我是外部类");
}
class inClass{
public void inMethod(){
System.out.println("我是内部类的方法");
}
}
}
public class BookTest {
public static void main(String[] args) {
Book book = new Book();
Book.inClass innerObject = book.new inClass();
innerObject.inMethod();
}
}
import java.lang.reflect.Method;
- 导入了Method
类,以便在后面使用 Java 反射 API。public class BookTest {
- 定义了名为BookTest
的公共类。public static void main(String[] args) throws Exception {
-main
方法是 Java 程序的入口点,在这里开始执行。方法的签名表明它可以抛出任何异常。Class<?> out = Class.forName("Book");
- 使用Class.forName()
方法加载名为 “Book” 的类。这会返回一个Class
对象,代表Book
类。Class<?> in = Class.forName("Book$inClass");
- 使用Class.forName()
方法加载名为 “Book i n C l a s s " 的内部类。内部类的名称由外部类的名称后面跟上 " inClass" 的内部类。内部类的名称由外部类的名称后面跟上 " inClass"的内部类。内部类的名称由外部类的名称后面跟上"” 符号,再跟上内部类的名称。inClass in1 = in.getDeclaredConstructors()[0].newInstance(out.newInstance());
- 创建内部类的一个实例。首先,通过in.getDeclaredConstructors()
获取内部类的所有构造函数,然后选择第一个(通常是默认构造函数)。然后,使用newInstance()
方法创建一个内部类的新实例,传递外部类的新实例作为参数,以确保内部类实例能够访问外部类的内容。Method method = in.getDeclaredMethod("inMethod");
- 使用getDeclaredMethod()
方法获取名为 “inMethod” 的方法的引用。这个方法被定义在内部类inClass
中。method.invoke(in1);
- 使用invoke()
方法调用inMethod()
方法。invoke()
方法接受要调用的对象(这里是内部类的实例)作为第一个参数。这行代码会执行内部类的inMethod()
方法。
import java.lang.reflect.Method;
public class BookTest {
public static void main(String[] args) throws Exception {
Class<?> out = Class.forName("Book");
Class<?> in = Class.forName("Book$inClass");
inClass in1 = in.getDeclaredConstructors()[0].newInstance(out.newInstance())
Method method = in.getDeclaredMethod("inMethod");
method.invoke(in1);
}
}
public class BookTest {
public static void main(String[] args) throws Exception {
// Load the classes
Class<?> out = Class.forName("Book");
Class<?> in = Class.forName("Book$inClass");
//在Java中,如果要通过反射创建内部类的实例,需要在创建内部类实例时提供外部类的实例作为参数。这是因为内部类实例在创建时需要与外部类实例相关联,以便能够访问外部类的成员变量和方法。
//通过out.newInstance()创建了一个外部类 Book 的实例。然后利用获取到的内部类 inClass 的构造函数,通过 in.getDeclaredConstructor(out) 获得了 inClass 的构造函数对象。最后,调用 newInstance() 方法,使用外部类 Book 的实例作为参数,创建了 inClass 的一个实例,这个实例被赋值给 inInstance 变量。
Object inInstance = in.getDeclaredConstructor(out).newInstance(out.newInstance());
Method method = in.getDeclaredMethod("inMethod");
method.invoke(inInstance);
}
}
7. 调用计算器
类方法调用:
import java.io.IOException;
public class calcTest {
public static void main(String[] args) throws IOException {
Runtime.getRuntime().exec("calc");
}
}
反射方法调用:
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class calcTest {
public static void main(String[] args) throws Exception {
Class<?> calcClass = Class.forName("java.lang.Runtime");
Constructor cons = calcClass.getDeclaredConstructor();
cons.setAccessible(true);
Runtime CalcIn = (Runtime) cons.newInstance();
Method runtimeMethod = calcClass.getDeclaredMethod("exec",String.class);
runtimeMethod.invoke(CalcIn,"calc");
}
}
getRuntime
方法是Runtime
类的静态方法,它返回一个Runtime
对象的实例。因此,我们可以将invoke
方法的第一个参数设为null
,因为我们不需要一个特定的对象实例来调用这个静态方法。
import java.lang.reflect.Method;
public class calcTest {
public static void main(String[] args) throws Exception {
Class<?> runtimeClass = Class.forName("java.lang.Runtime");
// 获取 getRuntime 方法
Method getRuntimeMethod = runtimeClass.getMethod("getRuntime");
// 调用 getRuntime 方法获取 Runtime 实例
Object runtimeObj = getRuntimeMethod.invoke(null);
// 获取 exec 方法
Method execMethod = runtimeClass.getMethod("exec", String.class);
// 调用 exec 方法执行命令
execMethod.invoke(runtimeObj, "calc");
}
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class calcTest {
public static void main(String[] args) throws Exception {
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(Class.forName("java.lang.Runtime").getMethod("getRuntime").invoke(null), "calc");
}
}