Java核心--反射(二)

本文深入讲解Java反射机制,包括如何获取类的构造器、方法,以及如何使用反射创建对象和调用方法等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.获取类中的构造器
需求:通过反射来获取一个类中的构造器
1)获取该类的字节码对象
2)从该字节码对象中寻找需要获取的构造器
class获取类的构造器的方法
constructor类:表示类中得构造器得类型,constructor得实例是某一个类中得某一个构造器
public  Constructor <?>[] getConstructors();该方法只能获取当前class所表示 类的public构造器
public  Constructor <?>[] getDeclaredConstructors();表示获取当前class中所有的构造器, 和访问权限无关;
public  Constructor < T > getConstructor( <?>... parameterTypes)表示获取当前class所表示类的一个指定public构造器
参数parameterTypes:构造参数的具体类型
如:public User(String name)
Constructor c = clz.getConstructor(String.class)
//获取指定的构造器
	private static void getOne() throws Exception, SecurityException {
		//1.获取类的字节码对象	
		Class<?> clz = user.class;
		//2.获取clz中指定的构造器
		//需求1:获取public user(){}
		Constructor<?> cs = clz.getConstructor();
		System.out.println(cs);
		//需求2:获取public user(String name){}
		Constructor<?> cs1= clz.getConstructor(String.class);
		System.out.println(cs1);
		//需求3:获取public user(String name , int age){}
		Constructor<?> cs2 = clz.getConstructor(String.class, int.class);
		System.out.println(cs2);
	}

2.使用反射获取构造器,创建对象
为什么使用反射创建对象?
因为在框架中,提供给我们的都是字符串
使用反射创建对象:
步骤:
(1)找到构造器所在类的字节码对象
(2)获取构造器对象
(3)使用反射创建对象
Constructor<?>表示构造器的类,constructor的实例表示一个类中的一个构造器
常用方法:
public T newInstance(Object... initargs) :如果调用带参数的构造器,只能使用该方法
参数initargs:表示调用构造器使用的实际参数
返回值T:返回创建的实例,T表示实际的class类型
如果一个类中的构造器可以直接访问,同时没有参数,那么可以直接使用class中的newInstance方法创建对象
class Person {
	public Person() {
		System.out.println("无参数构造器");
	}
	public Person(String name) {
		System.out.println("name构造器" + name);
	}
	private Person(String name, int age) {
		System.out.println("name,age构造器" + name + age);
	}
}
	public static void main(String[] args) throws Exception {
		// 获取不带参数的公共构造器,创建对象
		Class<Person> clz1 = Person.class;
		// 获取类中的构造器
		Constructor<Person> cs1 = clz1.getConstructor();
		// 使用newinstance方法来创建对象
		Person p = cs1.newInstance();
//无参,公共构造器,可以直接使用class类名.newinstance()
		clz1.newInstance();
		System.out.println(p);
		System.out.println("------------------------------");
		
		//获取带参数的公共构造器,并传入参数创建对象
		Class<Person> clz2= (Class<Person>) Class.forName("demo.Person");
		Constructor<Person> cs2 = clz2.getConstructor(String.class);
		Person person2 = cs2.newInstance("jimmy");
		System.out.println(person2);
		System.out.println("------------------------------");

		//获取带参数的私有构造器,并传入参数
		Constructor<Person> cs3 = clz2.getDeclaredConstructor(String.class,int.class);
		//设置当前构造器可以访问(反射暴力破坏了封装和访问权限)
		cs3.setAccessible(true);
/*没有这里的设置的话会有报错:
Exception in thread "main" java.lang.IllegalAccessException: Class demo.CreateObjectDemo can not access a member of class 			 				       	demo.Person with modifiers "private"
报错:不允许访问private修饰的构造器*/
		cs3.newInstance("allen",20);
		
	}
同理,构造器可以设置可访问,那么字段和方法都可以这样设置



3.使用反射获取类中的方法
步骤:
(1)获取方法所在类的字节码对象
(2)获取方法
----------------------------------------------
class类中常用方法:
public Method[ ] getMethods: 获取包括自身类和所有继承过来所有 public 方法
public Method[ ] getDeclaredMethods ;获取自身类中的所有方法(不包括继承的和 访问权限 无关)
getMethod(String name, 类<?>... parameterTypes):表示调用指定的一个公共方法(不包括继承的)
name:指定的方法名
parameterTypes:表示被调用方法的参数列表
返回:
the Method object that matches the specified name and parameterTypes
getDeclaredMethod(String name, 类<?>... parameterTypes) :
参数:
name- 指定的方法名
parameterTypes- 表示被调用方法的参数列表
返回:
the Method object for the method of this class matching the specified name and parameters
class User{
	public static void dowork(){
		System.out.println("dowork..");
	}
	public static void dowork(String a){
		System.out.println("dowork "+ a);
	}
	private String sayHello(String name){
		return "hello,"+name;
	}
}

//获取指定的方法
public static void getone() throws Exception {
		//需求获取dowork方法
		Class<?> clz= Class.forName("_method.User");
		Method method = clz.getMethod("dowork", String.class);
		System.out.println(method);
		//需求获取sayHello方法
		method=clz.getDeclaredMethod("sayHello", String.class);
		System.out.println(method);
		
	}
	//获取User中的全部方法
	public static void getAll() throws Exception {
		Class<?> cs1 = Class.forName("_method.User");
		Method[] methods= cs1.getMethods();
		for(Method m : methods){
			System.out.println(m);
		}

4.使用反射调用方法
1.调用对象方法
调用方法即是执行方法
如何调用一个方法:
在Method类中有方法:
public  Object invoke( Object obj, Object... args)表示调用当前method所表示的方法
obj - 被调用方法底层所属对象
args - 表示调用方法所传递的实际参数
返回:
底层方法的返回结果
调用私有方法:
调用前,应该设置其为可访问的
Method是Accessiable的子类,故Method有该方法
Method.setAccessiable(true);
// 调用dowork()方法
		Class<?> clz = Class.forName("_method.Person");
		Method m = clz.getMethod("dowork");
		System.out.println(m);
		m.invoke(clz.newInstance());
		System.out.println("-----------------------");
		
		// 调用dowork(String a)方法
		Method m2 = clz.getMethod("dowork",String.class);
		System.out.println(m2);
		m2.invoke(clz.newInstance(),"Lily");
		System.out.println("-----------------------");
		
		// 调用SayHello(String a)方法
		Method m3 = clz.getDeclaredMethod("sayHello",String.class);
		System.out.println(m3);
		//设置m3方法为可访问
		m3.setAccessible(true);
		Object ret= m3.invoke(clz.newInstance(),"Lily");
		System.out.println(ret);
2.调用类的静态方法
静态方法不属于任何对象,只属于类本身
此时把invoke方法的第一个参数设置为null即可

3.调用数组参数对象
王道:调用方法的时候把实际参数放到new Object{}数组的元素即可
Method.invoke(方法所属对象,new Object{所有实参})
class Employee {
	public static void doWork1(int[] arr) {
		System.out.println("dowork_:" + Arrays.toString(arr));
	}
	public static void doWork2(String... arr) {
		System.out.println("dowork_:" + Arrays.toString(arr));
	}
}

// 使用反射调用数组参数
public class _MethodInvokeDemo2 {
	public static void main(String[] args) throws Exception {
		//数组的类型是基本数据类型
		Class<?> clz1 = Class.forName("_method.Employee");
		Method method = clz1.getMethod("doWork1", int[].class);
		method.invoke(null,new int[]{1,2,3,4,5});
		
		//数组的类型是基本引用类型
		Method method2= clz1.getMethod("doWork2", String[].class);
		//method2.invoke(null,new String[]{"a","b","c","d"}); ERROR 引用数据类型调用的时候被自动解包
		method2.invoke(null,new Object[]{new String[]{"a","b","c","d"}});
		
	}
}

5.操作反射其他的API
Class类中:
int getModifies():获取修饰符
String getName():获取全限定名
Package getPackage():获取该类的包名
Class getSuperclass()获取类的父类
boolean isArray():判断是否为数组
boolean isEnum():判断是否为枚举
注意: 枚举类型不能通过反射获取类型和字段

6.加载资源文件路径:
db.properties
注意:加载properties方法只能用Properties类的load方法

使用相对路径-:
相对于 classpath的根路径( 字节码输出目录 ):
通过类加载器得到classpath的根路径.
resources是javaset中的 source folder, 这里面的文件会编译到 classpath的根路径
public static void main(String[] args) throws Exception {
		
		Properties  ps = new Properties();
 //方式1:通过线程类的获取加载器的方法,得到类加载器
	ClassLoader classld = Thread.currentThread().getContextClassLoader();
 //方式2:通过类名.class.获取加载器的方式
 // ClassLoader classld2 = ClassDemo.class.getClassLoader(); 
		InputStream ins =classld.getResourceAsStream("db.properties");
		ps.load(ins);
		System.out.println(ps);
	}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值