反射

反射
反射机制是在运行状态中
对于任意一个类,都能知道这个类的所有属性和方法
对于任意一个对象,都能调用他的任意一个方法和属性

反射提供的功能
在运行时判断任意一个对象所属于的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的方法
生成动态代理

反射入口 – 获取class对象

在java中有两种对象 — 实例对象和class对象,实例对象大家都会使用的比较多,就是我们平时直接new 出来的对象,通过对象可以get set属性值,还有执行这个对象所属类的一些方法等等操作,class对象对于我们来说可能是比较陌生的一个概念,但是class对象在反射中却被称之为入口对象,因为:
第一,class对象包含着与类有关的信息
第二,实例对象就是通过class对象来创建的
第三。每一个类只有一个class对象

如何获取class对象

1.class 的class.forName(“全类名”)无需从类的实例获取class对象

// 第一种方式:class.forName("全类名")
		Class personClass1 = Class.forName("com.it.reflect.Person");

2.类.class 通过class字面量获取class对象引用。相比其他两种,更加安全和简单

// 第二种方式: 类.class
		Class personClass2 = Person.class;

3.类对象.getClass() 需从类的实例获取class对象

Person person = new Person();
		Class personClass3 = person.getClass();
		

补充:

  1. 无论创建多少个实例对象,在JVM中都只有一个Class对象,即在内存中每个类有且只有一个相对应的Class对象,说通俗点就是,class对象保存了这个类所有的类型信息,每一个实例对象就是从这个class获取信息的,如图所示:
    在这里插入图片描述
  2. class类只存在私有构造函数
  3. class类的对象作用是在运行时提供或获得某个对象的类型信息

通过反射获取方法

  1. 获取所有公共方法(本类,父类以及父接口的公共方法)
		// 获取所有公有方法
		Method[] methods = personClass.getMethods();
  1. 获取所有的方法(包括私有方法)
		// 获取所有方法(包括私有方法)
		Method[] allMethod = personClass.getDeclaredMethods();

完整如下:

public static void getMethodDemo() {
		
		// 获取class对象,进入反射入口
		Class personClass = Person.class;
		
		// 获取所有公有方法
		Method[] methods = personClass.getMethods();
		for(Method m : methods) {
			System.out.println("Person所有方法如下:"+m+"\n");
		}
		
		// 获取所有方法(包括私有方法)
		Method[] allMethod = personClass.getDeclaredMethods();
		for(Method m : allMethod) {
			System.out.println("Person所有方法包括私有方法如下:"+m+"\n");
		}
		
	}

结果:

Person所有方法如下:public java.lang.String com.it.reflect.Person.getName()

Person所有方法如下:public int com.it.reflect.Person.getId()

Person所有方法如下:public void com.it.reflect.Person.setName(java.lang.String)

Person所有方法如下:public int com.it.reflect.Person.getAge()

Person所有方法如下:public void com.it.reflect.Person.publicMethod()

Person所有方法如下:public void com.it.reflect.Person.setAge(int)

Person所有方法如下:public void com.it.reflect.Person.setId(int)

Person所有方法如下:public final void java.lang.Object.wait() throws java.lang.InterruptedException

Person所有方法如下:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException

Person所有方法如下:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException

Person所有方法如下:public boolean java.lang.Object.equals(java.lang.Object)

Person所有方法如下:public java.lang.String java.lang.Object.toString()

Person所有方法如下:public native int java.lang.Object.hashCode()

Person所有方法如下:public final native java.lang.Class java.lang.Object.getClass()

Person所有方法如下:public final native void java.lang.Object.notify()

Person所有方法如下:public final native void java.lang.Object.notifyAll()

Person所有方法包括私有方法如下:public java.lang.String com.it.reflect.Person.getName()

Person所有方法包括私有方法如下:public int com.it.reflect.Person.getId()

Person所有方法包括私有方法如下:public void com.it.reflect.Person.setName(java.lang.String)

Person所有方法包括私有方法如下:private void com.it.reflect.Person.privateMethod()

Person所有方法包括私有方法如下:public int com.it.reflect.Person.getAge()

Person所有方法包括私有方法如下:public void com.it.reflect.Person.publicMethod()

Person所有方法包括私有方法如下:public void com.it.reflect.Person.setAge(int)

Person所有方法包括私有方法如下:public void com.it.reflect.Person.setId(int)


获取所有的接口

		// 获取class对象,进入反射入口
		Class personClass = Person.class;
		
		// 获取所有接口
		System.out.println("获取所有接口:"+personClass.getInterfaces());

获取所有父类,并且获取父类的所有公共方法

public static void getSuper() {
		
		// 获取class对象,进入反射入口
		Class personClass = Person.class;
		
		Class superClass = personClass.getSuperclass();
		
		Method[] method = superClass.getMethods();
		
		for(Method m : method) {
			System.out.println("父类的所有公共方法:"+m);
		}
	}
	
	

由于Person没有继承其他类,所以他的父类默认就是Object

父类的所有公共方法: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()

获取该类的构造方法

		// 获取所有构造方法
		Constructor[] constructor = personClass.getDeclaredConstructors();

通过构造方法获取实例对象

		// 通过无参构造方法实例化对象
		Person person = (Person) personClass.getConstructor().newInstance();
		
		// 通过有参构造方法实例化对象
		Person person2 = (Person) personClass.getConstructor(int.class,String.class).newInstance(21,"kevin");

通过查看获取类构造方法这个方法

Constructor<T> getConstructor(Class<?>... parameterTypes)

可以看到,这个方法的参数列表是一个可变参数,当不传参时,会找出无参构造方法,也可以指定具体参数,参数传得是参数类型的class对象,找具体的构造方法后,通过newInstance()这个方法便可以构造出实例对象,当调用有参构造时,newInstance()里传得就是具体的参数值

补充:
这两个一样吗:

personClass.getConstructor(int.class,String.class).newInstance(21,"kevin");

personClass.getConstructor(Integer.class,String.class).newInstance(21,"kevin");

这样会报错,在反射中,根据类型获取方法时:基本类型和包装类是不可以通用的,是两种类型

结果:

person所有构造方法:public com.it.reflect.Person()
person所有构造方法:public com.it.reflect.Person(int,java.lang.String)
person 无参构造
有参构造:21  kevin

获取所有的属性

  1. 获取所有的公共属性
Field[] fields = personClass.getFields();
  1. 获取所有的属性(包括私有属性)
Field[] fields = personClass.getDeclaredFields();
  1. 获取指定属性并进行赋值
Person person = (Person) personClass.newInstance();
		// 获取指定字段
		Field field = personClass.getDeclaredField("id");
		// 消除private 的影响
		field.setAccessible(true);
		// 为字段赋值
		field.set(person, 21);
		System.out.println("ID"+person.getId());

结果:

person 无参构造
ID21

反射可以越过泛型检查 — 实际开发不推荐

public static void crossT() throws Exception {
		
		ArrayList<String> list = new ArrayList<>();
		list.add("a");
		list.add("b");
		list.add("c");
		
		Class listClass = list.getClass();
		
		Method method = listClass.getMethod("add", Object.class);
		
		method.invoke(list, 123);
		
		System.out.println(list.size());
	}

输出结果:4
可以看到,现在的list中不单单可以添加String类型的元素 ,可以添加元素的范围大到Object,但是在实际开发中并不建议这么使用,因为这会使得程序不安全

如何去理解反射?

  1. 第一,首先应该理解什么是反,为什么是反?
    原来我们仔使用某个类的时候必定知道它是什么类,用来干什么,于是,我们直接对他用new进行实例化,这就是 正 ,但是现在要求我们在不知道要初始化的类对象是什么的时候,自然就无法通过new来实例化对象,这时候就需要用到反射的API 进行反射调用
  2. 现在很多流行的框架都用了反射,例如spring框架的ioc原理重点就是反射,举个例子好理解:
    比如订餐程序 美团外卖,在下单支付的时候,我们可以通过支付宝支付,可以通过微信支付,可以通过银行卡直接 等等途径。那么在美团外卖和这些支付的公司合作的时候,就一定要制定一个支付的准则,这个准则,在java中怎么表示?自然是用接口了,所以由美团外卖指定接口,然后这些合作的公司去实现这个接口,现在有两个类实现了这个接口,一个类是支付宝支付,另一个是微信支付,你作为用户来说,你肯定是要么用支付宝支付,要么用微信支付,二选一。但是难道你说用支付宝支付,然后你自己过来改个源码,然后明天支付宝没钱了你用微信支付,你又跑来改源码,说你用微信支付。。这不可能把?你可能想,那么可以让程序员在定义的时候,就用if-else进行判断,但是这样,代码的可扩展性就不好,如果哪天要来个京东支付或者什么银行卡支付,这又得加多几个 else if ,这会很耗时间和精力,但是如果通过反射,让客户在选择支付方式的时候,前台传过来接口实现类的全类名,在通过class.forName()构造出相应实例,调用相应支付方法,这样代码的可扩展性就很好了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值