【Java】反射基础——示范详解

目录

反射概述

Class对象的由来

类加载器

JVM自带的类加载器的组成

类什么时候被加载(进入内存)

类的生命周期

引用关系

类的卸载

反射

Class对象的基本使用

扫描二维码关注公众号,回复: 9884863 查看本文章

获取Class对象的3中方式

反射获取构造方法并创建对象

反射获取成员字段

反射获取成员方法

反射练习

一、通过反射越过泛型检查

二、通过配置文件创建指定对象、并调用指定方法


反射概述

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有成员(公有、私有、默认、保护);对于任意一个对象,都能够调用它的任意一个方法和属性;

而对类的解剖类就是.class字节码文件的Class对象。

Class对象解剖包括:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖。

Class对象的由来

类加载器

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三个步骤来实现对这个类进行初始化。

加载:

  • 将class文件读入内存,并为之建立一个Class对象。
  • 一个二进制字节码文件对应的Class对象只能有一个,除非使用的是自定义加载器。

连接:

  • 验证. 是否有正确的内部结构,并且和其他类协调一致。
  • 准备. 负责为类的静态成员分配内存,并设置默认的初始化值。
  • 解析. 将类的二进制数据中的符号引用替换为直接引用。

初始化

JVM自带的类加载器的组成

  1. Bootstrap ClassLoader 根类加载器
    也被称为引导类加载器,负责Java核心类的加载(jre的lib目录下rt.jar)。
  2. Extension ClassLoader 扩展类加载器
    负责JRE的扩展目录中jar包的加载(ext文件夹下)。
  3. System ClassLoader 系统类加载器
    负责在JVM启动时在加载来自java命令的class文件,以及第三方jar文件。

类什么时候被加载(进入内存)

  • 直接使用java.exe命令来运行某个主类。
  • 初始化某个类的子类,其父类先被加载。
  • 创建类的实例(new对象)。
  • 使用类的静态变量,或者为静态变量赋值。
  • 调用类的静态方法。
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。

最先进入内存的类是Object类。

类的生命周期

当类被加载、连接和初始化后,它的生命周期就开始了。

当类的Class对象不再被引用,Class对象就会结束生命周期,类在方法区内的数据也会被卸载,从而结束类的生命周期。

由此可见,一个类何时结束生命周期,取决于代表它的Class对象何时结束生命周期

引用关系

加载器和Class对象:

  • 在类加载器的内部实现中,用一个Java集合来存放所加载类的引用。
  • 另一方面,一个Class对象总是会引用它的类加载器。调用Class对象的getClassLoader()方法,就能获得它的类加载器。
  • 由此可见,Class实例和加载它的加载器之间为双向关联关系。(互相引用)

类、类的Class对象、类的实例对象:

  • 一个类的实例总是引用代表这个类的Class对象。
  • 在Object类中定义了getClass()方法,这个方法返回代表对象所属类的Class对象的引用。
  • 此外,所有的Java类都有一个静态属性class,它引用代表这个类的Class对象。

类的卸载

  • 由Java虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。
  • Java虚拟机本身会始终引用这些类加载器,而这些类加载器则会始终引用它们所加载的类的Class对象,因此这些Class对象始终是可触及的
  • 由用户自定义的类加载器加载的类是可以被卸载的。

        loader1变量和obj变量间接应用代表Sample类的Class对象,而objClass变量则直接引用它。

   如果程序运行过程中,将上图左侧三个引用变量都置为null,此时Sample对象结束生命周期,MyClassLoader对象结束生命周期,代表Sample类的Class对象也结束生命周期,Sample类在方法区内的二进制数据被卸载

  当再次有需要时,会检查Sample类的Class对象是否存在,如果存在会直接使用,不再重新加载;如果不存在Sample类会被重新加载,在Java虚拟机的堆区会生成一个新的代表Sample类的Class实例 (可以通过哈希码查看是否是同一个实例)。

反射

Class对象的基本使用

获取Class对象的3中方式

  1. 使用继承自Object类的 ——> getClass();方法

  2. 任何数据类型(包括基本数据类型)都有一个“静态”的class属性。

  3. 通过Class类的静态方法:forName(String  className)(常用)。

package com.bin.demo;

public class Main {

	public static void main(String[] args) throws ClassNotFoundException {
		//第一种方式获取Class对象
		Instance obj1 = new Instance();
		Class<?> c1 = obj1.getClass();
		System.out.println(c1.getName());
		//第二种方式获取Class对象
		Class<?> c2 = Instance.class;
		System.out.println(c2 == c1);
		//第三种方式获取Class对象
		//绝对路径 | 相对路径 + 全限定类名
		Class<?> c3 = Class.forName("com.bin.demo.Instance");
		System.out.println(c3 == c2);
	}

}

反射获取构造方法并创建对象

相关方法

// 获得所有 公共构造方法
public Constructor<?>[] getConstructors()
// 获取指定公共构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
// 获取所有构造方法包括(公共、私有、保护、默认)
public Constructor<?>[] getDeclaredConstructors()
// 获取指定的构造方法,可以是(公共、私有、保护、默认)
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
//调用构造对象Constructor的方法,可以传参数,也可以不传
public T newInstance(Object... obj)

Instance.java类

package com.bin.demo;

public class Instance {
	
	//默认构造方法
	Instance(String str){
		System.out.println("默认构造 value = " + str);
	}
	
	//无参构造方法
	public Instance(){
		System.out.println("公有无参构造");
	}
	
	//有一个参数的构造方法
	public Instance(Integer age){
		System.out.println("年龄:" + age);
	}
	
	//有多个参数的构造方法
	public Instance(String name ,int age){
		System.out.println("姓名:" + name + "年龄:" + age);
	}
	
	//受保护的构造方法
	protected Instance(boolean n){
		System.out.println("受保护构造 n = " + n);
	}
	
	//私有构造方法
	private Instance(int age){
		System.out.println("私有构造  年龄:"+ age);
	}

}
package com.bin.demo;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Main {

	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		
		Class<?> c = Class.forName("com.bin.demo.Instance");
		
		System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
		Constructor[] conArray = c.getDeclaredConstructors();
		for (Constructor cc : conArray) {
			System.out.println(cc);
		}
		
		System.out.println("**********************所有公有构造方法*********************************");
		conArray = c.getConstructors();
		for (Constructor<?> cc : conArray) {
			System.out.println(cc);
		}

		System.out.println("*****************获取公有指定构造方法*******************************");
		Constructor con = c.getConstructor(null);
		//穿null或不写就默认查找无参公有构造,这里需要的是可变参数的类型
		System.out.println("con = " + con);

		System.out.println("******************获取指定构造方法:并调用*******************************");
		con = c.getDeclaredConstructor(Integer.class);
		System.out.println(con);
		con.setAccessible(true); //解除私有权限,对构造函数来说可以不更改此权限
		Object obj = con.newInstance(100);

	}

}

输出

************所有的构造方法(包括:私有、受保护、默认、公有)***************
private com.bin.demo.Instance(int)
protected com.bin.demo.Instance(boolean)
public com.bin.demo.Instance(java.lang.String,int)
public com.bin.demo.Instance(java.lang.Integer)
public com.bin.demo.Instance()
com.bin.demo.Instance(java.lang.String)

**********************所有公有构造方法*********************************
public com.bin.demo.Instance(java.lang.String,int)
public com.bin.demo.Instance(java.lang.Integer)
public com.bin.demo.Instance()

*****************获取公有指定构造方法*******************************
con = public com.bin.demo.Instance()

******************获取指定构造方法:并调用*******************************
public com.bin.demo.Instance(java.lang.Integer)
年龄:100

反射获取成员字段

相关方法

//获取所有公共的变量
public Field[] getFields()
//获取指定的公共变量,传递参数是字符串形式的变量名
public Field getField(String name)
//获取指定变量,可以是(公共、私有、保护、默认)
public Field getDeclaredField(String name)
//获取所有变量,包括(公共、私有、保护、默认),不包括继承的字段
public Field[] getDeclaredFields()
// Field对象的方法,第一个参数:要改变与此Field变量对象的对象。第二个参数:改变的值。
public void set(Object obj, Object value)

Instance.java类

package com.bin.demo;

public class Instance {
	
	public String name;
	protected int age;
	boolean isEnd;
	private String phoneNum;
	
	public String getPhoneNum() {
		return phoneNum;
	}

}
package com.bin.demo;

import java.lang.reflect.Field;

public class Main {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("com.bin.demo.Instance");
		
		System.out.println("************获取所有的字段(公有、私有、受保护、默认的)********************");
		Field[] fieldArray = c.getDeclaredFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}

		System.out.println("************获取所有公有的字段********************");
		fieldArray = c.getFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}
		
		System.out.println("*************获取指定公有字段**并设置值和调用***********************************");
		Field f = c.getField("name");
		System.out.println(f);
		//创建一个对象
		Object obj = c.getConstructor().newInstance();
		//为字段设置值
		f.set(obj, "超人");
		//验证
		Instance instance = (Instance) obj;
		System.out.println("验证姓名:" + instance.name);
		
		System.out.println("**************获取指定字段****并设置值和调用********************************");
		f = c.getDeclaredField("phoneNum");
		System.out.println(f);
		f.setAccessible(true); //解除私有权限,改变私有变量需要解除权限
		f.set(obj, "110");
		System.out.println("验证电话:" + instance.getPhoneNum());
	}

}

输出

************获取所有的字段(公有、私有、受保护、默认的)********************
public java.lang.String com.bin.demo.Instance.name
protected int com.bin.demo.Instance.age
boolean com.bin.demo.Instance.isEnd
private java.lang.String com.bin.demo.Instance.phoneNum

************获取所有公有的字段********************
public java.lang.String com.bin.demo.Instance.name

*************获取指定公有字段**并设置值和调用***********************************
public java.lang.String com.bin.demo.Instance.name
验证姓名:超人

**************获取指定字段****并设置值和调用********************************
private java.lang.String com.bin.demo.Instance.phoneNum
验证电话:110

反射获取成员方法

相关方法

//获取所有公共方法
public Method[] getMethods()
//获取指定公共的方法
public Method getMethod(String name, Class<?>... parameterTypes)
//获取所有方法,包括(公共、私有、默认、保护),但不包括继承的方法
public Method[] getDeclaredMethods()
//获取指定的方法,包括(公共、私有、保护、默认)
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
//调用此方法,第一个参数:要调用的对象。第二个参数:传递的值,是个可变参数。
public Object invoke(Object obj, Object... args)

Instance.java类

package com.bin.demo;

public class Instance {
	
	public void show1(String s){
		System.out.println("调用了:公有的,String参数的show1(): s = " + s);
	}
	protected void show2(){
		System.out.println("调用了:受保护的,无参的show2()");
	}
	void show3(){
		System.out.println("调用了:默认的,无参的show3()");
	}
	private String show4(int age){
		System.out.println("调用了,私有的,并且有返回值String的,int参数的show4(): age = " + age);
		return "返回了String哦";
	}

}
package com.bin.demo;

import java.lang.reflect.Method;

public class Main {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName("com.bin.demo.Instance");
		
		System.out.println("***************获取所有公有方法、包括继承的公有方法*******************");
		Method[] methodArray = c.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		
		System.out.println("***************获取所有方法,包括公有、私有、保护、默认*******************");
		methodArray = c.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		
		System.out.println("***************获取指定公有show1()方法并调用*******************");
		Method m = c.getMethod("show1", String.class);
		System.out.println(m);
		//创建一个对象
		Object obj = c.getConstructor().newInstance();
		m.invoke(obj, "超人");
		
		System.out.println("***************获取指定私有的show4()方法并调用******************");
		m = c.getDeclaredMethod("show4", int.class);
		System.out.println(m);
		m.setAccessible(true); //解除私有权限,必须
		Object result = m.invoke(obj, 20);
		System.out.println("返回值:" + result);
	}

}

输出

***************获取所有公有方法、包括继承的公有方法*******************
public void com.bin.demo.Instance.show1(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

***************获取所有方法,包括公有、私有、保护、默认*******************
public void com.bin.demo.Instance.show1(java.lang.String)
private java.lang.String com.bin.demo.Instance.show4(int)
protected void com.bin.demo.Instance.show2()
void com.bin.demo.Instance.show3()

***************获取指定公有show1()方法并调用*******************
public void com.bin.demo.Instance.show1(java.lang.String)
调用了:公有的,String参数的show1(): s = 超人

***************获取指定私有的show4()方法并调用******************
private java.lang.String com.bin.demo.Instance.show4(int)
调用了,私有的,并且有返回值String的,int参数的show4(): age = 20
返回值:返回了String哦

反射练习

一、通过反射越过泛型检查

被提了无数次的例子

package com.bin.demo;

import java.util.ArrayList;

public class Main {

	public static void main(String[] args) throws Exception {
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		list.add(3);
		
		ArrayList.class.getMethod("add", Object.class).invoke(list, "我是入侵者");
		
		for (Object obj : list) {
			System.out.println(obj);
		}
	}

}

输出

1
2
3
我是入侵

二、通过配置文件创建指定对象、并调用指定方法

Instance.java类

package com.bin.demo;

public class Instance {
	
	public void show() {
		System.out.println("调用了show()方法");
	}
	
}

配置文件pro.txt

classPath=com.bin.demo.Instance
methodName=show
package com.bin.demo;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Main {

	public static void main(String[] args) throws Exception {
		Class<?> c = Class.forName(getValue("classPath"));
		Method m = c.getMethod(getValue("methodName"));
		m.invoke(c.newInstance());
	}
	
	// 此方法接收一个key,在配置文件中获取相应的value
	public static String getValue(String key) throws IOException {
		Properties pro = new Properties();// 获取配置文件的对象
		FileReader in = new FileReader("F:\\pro.txt");// 获取输入流
		pro.load(in);// 将流加载到配置文件对象中
		in.close();
		return pro.getProperty(key);// 返回根据key获取的value值
	}

}

输出

调用了show()方法
发布了27 篇原创文章 · 获赞 33 · 访问量 9500

猜你喜欢

转载自blog.csdn.net/qq_42470947/article/details/104565393