Java反射

一、什么是反射?

Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。而这也是Java被视为动态(或准动态,为啥要说是准动态,因为一般而言的动态语言定义是程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。)语言的一个关键性质。

什么是反射?上面的话可能说的太官方,那我来用自己的话来说一下:就是我们长写的db数据库连接类,Java肯定是可以连接多个数据的,这里就用到了Java反射,因为加Java不知道我们用户是需要使用哪一个数据库,所以Java在这里就用了Java反射的动态加载。一句话总结:反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

下面我们再来说一说为什么要用反射?

二、为什么要用反射?

Java Reflection功能非常强大,并且非常有用,比如:

  • 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
  • 获取任意对象的属性,并且能改变对象的属性
  • 调用任意对象的方法
  • 判断任意一个对象所属的类
  • 实例化任意一个类的对象
  • 通过反射我们可以实现动态装配,降低代码的耦合度,动态代理等。

接下来我们在说一下我们等一下会用得Class对象

三、Class对象介绍

大写的Class类到底是什么?
请看我下面这张图
在这里插入图片描述
理解到这里接下来我们在来学习我们怎么获取Class对象
有三种方式:
1、Class.forName(完整类名)
2、类名 . class
3、对象 . getClass()

下面是三种获取Class对象的代码演示(带详细注释):

package com.zking.reflect;

public class Demo {
@SuppressWarnings("rawtypes")//压制黄色的线
public static void main(String[] args) throws Exception {
	//这里有一个强制异常,就是帮我们加载这个类的时候很有可能这
	//个类不存在,所以就有一个强制异常,解决方法,要么try_catch,
	//要么使用throws 明确的抛出,(这是处理强制异常常用的两种方式),我们这里使用的是明确的抛出 
	
	//第 1 种获取Class对象的方法:Class.forName()
	Class<?> clazz01 = Class.forName("com.zking.reflect.Student");//括号里面要放 权限定名
	
	//第 2 种:类.class
	Class clazz02 = Student.class;
	
	//第 3 种:对象.getClass()
	Student stu = new Student();
	Class clazz03 = stu.getClass();
	
	//打印一下是否是获取的同一个对象
	System.out.println(clazz01);
	System.out.println(clazz02);
	System.out.println(clazz03);
	
}
}

这里是Student对象

package com.zking.reflect;

public class Student {
	private String sid;

	private String sname;

	public Integer age;
	
	/**
	 * 静态代码块
	 */
	static{
		System.out.println("加载进jvm中!");
	}

	/**
	 * 无参的构造函数
	 */
	public Student() {
		super();
		System.out.println("调用无参构造方法创建了一个学生对象");
	}

	/**
	 * 有参的构造函数
	 * @param sid
	 */
	public Student(String sid) {
		super();
		this.sid = sid;
		System.out.println("调用带一个参数的构造方法创建了一个学生对象");
	}

	/**
	 * 两个参数的构造函数
	 * @param sid
	 * @param sname
	 */
	public Student(String sid, String sname) {
		super();
		this.sid = sid;
		this.sname = sname;
		System.out.println("调用带二个参数的构造方法创建了一个学生对象");
	}

	/**
	 * 私有化的构造函数
	 * @param age
	 */
	@SuppressWarnings("unused")
	private Student(Integer age) {
		System.out.println("调用Student类私有的构造方法创建一个学生对象");
		this.age = age;
	}

	/**
	 * 下面是get和set方法
	 * @return
	 */
	public String getSid() {
		return sid;
	}

	public void setSid(String sid) {
		this.sid = sid;
	}

	public String getSname() {
		return sname;
	}

	public void setSname(String sname) {
		this.sname = sname;
	}

	/**
	 * 无参的方法
	 */
	public void hello() {
		System.out.println("你好!我是" + this.sname);
	}

	/**
	 * 有参的构造方法
	 * @param name
	 */
	public void hello(String name) {
		System.out.println(name + "你好!我是" + this.sname);
	}
	
	/**
	 * 私有的方法
	 * @param a
	 * @param b
	 * @return
	 */
	@SuppressWarnings("unused")
	private Integer add(Integer a, Integer b) {
		return new Integer(a.intValue() + b.intValue());
	}

	@Override
	public String toString() {
		return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
	}
	
	
}

四、反射实例化、反射动态方法调用、反射读写属性

这里我们是使用的第一种方法获取的Class对象 :Class.forName()
上代码之前我们先了解一下这几个东西
getConstructor() 意思是获取公开的构造函数

newInstance() 意思是获取一个实例

getDeclaredConstructor 意思是既可以获取公开的构造函数也可以获取私有的构造函数

setAccessible(true) 设置访问控制符

getMethod(name, parameterTypes) 获取方法,name是填方法名,parameterTypes是填可变参数(没有就不填) 只能获取公开的

invoke 调用

getDeclaredMethod 既可以调用公开的也可以调用私有的

getField() 获取属性,只能获取公开的

getDeclaredField 公开的,私有的都能获取
接下来我们来上代码(代码的每一步都有详细的注释)

package com.zking.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Demo {
@SuppressWarnings("rawtypes")//压制黄色的线
public static void main(String[] args) throws Exception {
	//这里有一个强制异常,就是帮我们加载这个类的时候很有可能这
	//个类不存在,所以就有一个强制异常,解决方法,要么try_catch,
	//要么使用throws 明确的抛出,(这是处理强制异常常用的两种方式),我们这里使用的是明确的抛出 
	
	//第 1 种获取Class对象的方法:Class.forName()
	Class<?> clazz01 = Class.forName("com.zking.reflect.Student");//括号里面要放 权限定名
	
	//第 2 种:类.class
	Class clazz02 = Student.class;
	
	//第 3 种:对象.getClass()
	Student stu = new Student();
	Class clazz03 = stu.getClass();
	
	System.out.println(clazz01);
	System.out.println(clazz02);
	System.out.println(clazz03);
	
	
//--这里是获取的 无参 的构造函数------------------------------------------------------------
	//通过反射实例化对象
	//getConstructor()意思是获取公开的构造函数
	Constructor<?> c1 = clazz01.getConstructor();//这是构造函数对象
	//newInstance()意思是获取一个实例
	Student stu01 = (Student)c1.newInstance();//因为我们获取的是Student对象,所以这里要强转
	stu01.setSname("李康");
	System.out.println(stu01);
	//这里就表示,我们反射出来生成的对象是可以操作的
//----------------------------------------------------------------------------------

	
	
//--这里是获取的 有参 的构造函数------------------------------------------------------------
	Constructor<?> c2 = clazz01.getConstructor(String.class);//这是构造函数对象
	Student stu02 = (Student)c2.newInstance("007");
	stu02.setSname("周丽桥");
	System.out.println(stu02);
	
//----------------------------------------------------------------------------------
	
	
	
//--这里是获取的 两个参数 的构造函数------------------------------------------------------------
	Constructor<?> c3 = clazz01.getDeclaredConstructor(String.class,String.class);//这是构造函数对象
	Student stu03 = (Student)c3.newInstance("008","张三");
	System.out.println(stu03);
//----------------------------------------------------------------------------------

	
	
//--这里是获取的 私有 的构造函数------------------------------------------------------------
	//getDeclaredConstructor意思是既可以获取公开的构造函数也可以获取私有的构造函数
	Constructor<?> c4 = clazz01.getDeclaredConstructor(Integer.class);//这是构造函数对象
	c4.setAccessible(true);//访问控制符
	Student stu04 = (Student)c4.newInstance(18);
	System.out.println(stu04);
	
//----------------------------------------------------------------------------------	


	
/**
 * 接下来就是学习  反射动态方法的调用
 */
//--这里是调用的 无参 的 方法------------------------------------------------------------
	//getMethod(name, parameterTypes)  获取方法,name是填方法名,parameterTypes是填可变参数(没有就不填)
	Method method01 = clazz01.getMethod("hello");//这是一个方法对象
	//因为hello是一个实例方法,所以我们要搞一个对象
	method01.invoke(stu04);//这里的意思就是,我们调用stu04对象上的hello方法
//----------------------------------------------------------------------------------



//--这里是调用的 有参 的 方法------------------------------------------------------------
	Method method02 = clazz01.getMethod("hello",String.class);//这是一个方法对象
	method02.invoke(stu04, "猪猪");
//----------------------------------------------------------------------------------	
	
	
	
//--这里是调用的 私有 的 方法------------------------------------------------------------
	Method method03 = clazz01.getDeclaredMethod("add",Integer.class,Integer.class);//这是一个方法对象
	method03.setAccessible(true);//访问控制符
	//方法是return的,所以要获取return的值
	int rv = (int)method03.invoke(stu04, 1,1);
	System.out.println(rv);
//----------------------------------------------------------------------------------	

	
	
/**
 * 在就是学习 反射读写属性	
 */
	//getField() 获取属性,只能获取公开的
	//getDeclaredField 公开的,私有的都能获取
	Field f = clazz01.getField("age");
	f.set(stu04, 78);
	System.out.println(stu04);
	System.out.println(f.get(stu04));
	
	
	Field f01 = clazz01.getDeclaredField("sname");
	f01.setAccessible(true);
	f01.set(stu04, "张飒");
	System.out.println(stu04);
}
}

五、调用私有的需要注意的问题

调用私有的一定要加 Declared
在这里插入图片描述
如果不加就会报没有这个方法的异常,如下图
在这里插入图片描述
加了 Declared 还不算完
还要 .setAccessible(true) 设置访问控制符为true
在这里插入图片描述
如果不设置就会报不合法的访问异常,如下图
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值