一、什么是反射?
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
如果不设置就会报不合法的访问异常,如下图