1、什么是反射
反射是使用"类"的一种方式,它可以使我们的程序在运行时,动态的去加载某个类,然后去获取它内部的成员信息,进而可以创建这个类对象,并且调用它的成员。反射的主要作用是解开类和类之间的耦合关系,有利于后期开发的更新与迭代。下面使用代码讲解一下具体的实现机理:
之前使用类,先创建对象:
创建Student类
public class Student {//创建一个学生对象
public Student(){
System.out.println("创建了一个Student对象");
}
}
编写测试类
/*
class文件的加载:
*/
public class Demo02 {
public static void main(String[] args) throws InterruptedException {
Student stu1 = new Student();//第一次创建对象
for (int i = 0; i < 15; i++) {
System.out.println("i = " + i);
Thread.sleep(1000);
}
Student stu2 = new Student();//第二次创建对象
Student stu3 = new Student();//第三次创建对象
}
}
内存中创建对象的实现机理
//1.运行main方法,查看方法区里面是否存在Class类的对象的相关信息,如果没有。执行第二步;
//2.从磁盘里面加载Student类的相关信息进内存,执行第三步;
//3.在方法区中创建一个Class类的对象Student,包括Student类的构造方法,成员属性,成员方法等
//4.在堆中开辟Stu1的内存空间
//5.当main函数运行到第二次创建student对象时,
//6.jvm直接在方法区中访问student类,
//7.在栈中开辟第二块Student地址空间。
这样创建对象后,会使我们的类与被使用的类产生了"依赖"关系。不利于后期的更新、维护。
使用反射以后:
如上图的绿色线,使用反射技术以后,系统不经过JVM虚拟机,直接获取CLASS对象,如果存在Class对象,获取这个对象,然后操作这个对象,如果不存在Class对象,先创建一个Class对象,然后再对这个Class对象进行操作。
2.Class类
从jdk1.6文档中可以知道,Class类是java.lang下面的一个类,继承于object类,实现了Serializable, AnnotatedElement, GenericDeclaration, Type四个接口,并且Class类没有公共的构造方法。
Class类的常用方法
1).获取构造方法:
A).批量获取:
1).public Constructor[] getConstructors():获取所有的"公有"构造方法。
2).public Constructor[] getDeclaredConstructors():获取所有的构造方法,包括公有、受保护、默认、私有的。
B).获取单个:
3).public Constructor getConstructor(Class<?>... parameterTypes):获取某个公有构造方法
4).public Constructor getDeclaredConstructor(Class<?>... parameterTypes):获取某个构造方法,可以是:公有、受保护、默认、私有
-创建对象:通过调用Constructor的newInstance(Object ... params)方法来进行对象的新建
注意:
如果没有访问权限,要先设置一下:暴力访问
Constructor-->setAccessible(true):
代码示例:
创建学生类,分别设置无参构造、有参构造、受保护的构造、默认的构造、私有的构造方法
public class Student {
public Student(){
System.out.println("调用了公有、无参的构造方法...");
}
public Student(int a) {
System.out.println("调用了公有、int参数的构造方法...");
}
protected Student(String string) {
System.out.println("调用了受保护、String参数的构造方法...");
}
Student(double d) {
System.out.println("调用了默认的、double参数的构造方法...");
}
private Student(String name, int age) {
System.out.println("调用了私有的、String、int参数的构造方法...String = " + name + " age = " + age);
}
main方法
import java.lang.reflect.Constructor;
/*
通过Class获取类的构造方法:
A).批量获取:
1).public Constructor[] getConstructors():获取所有的"公有"构造方法。
2).public Constructor[] getDeclaredConstructors():获取所有的构造方法,包括公有、受保护、默认、私有的。
B).获取单个:
3).public Constructor getConstructor(Class<?>... parameterTypes):获取某个公有构造方法
4).public Constructor getDeclaredConstructor(Class<?>... parameterTypes):获取某个构造方法,可以是:公有、受保护、默认、私有
*/
public class Demo04 {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("com.itheima.demo04.Student");
System.out.println("获取所有[共有]构造方法:");
Constructor[] cs = stuClass.getConstructors();//获取Student类里面所有的公有构造方法
for (Constructor c : cs) {
System.out.println(c);
}
System.out.println("获取[所有的]构造方法:");
Constructor[] cs2 = stuClass.getDeclaredConstructors();
for (Constructor constructor : cs2) {
System.out.println(constructor);
}
System.out.println("获取公有、无参的构造方法:");
Constructor c1 = stuClass.getConstructor();
System.out.println(c1);
System.out.println("获取公有、int参数的构造方法:");
Constructor c2 = stuClass.getConstructor(int.class);
System.out.println(c2);
System.out.println("获取私有的,String和int参数的构造方法:");
Constructor c3 = stuClass.getDeclaredConstructor(String.class, int.class);
System.out.println(c3);
//获取到class类的构造方法后,还需要创建对象,在创建的同时,也可以通过有参构造,经行对对象的赋值。但是创建的对象想要访问私有的构造方法,需要设置暴力访问,暴力访问的主要作用是不进行权限修饰符的验证。
System.out.println("【创建对象】调用公有、无参构造:");
Object o1 = c1.newInstance();
System.out.println("【创建对象】调用公有、int参数的构造方法:");
Object o2 = c2.newInstance(22);
System.out.println("【创建对象】调用私有、String、int参数的构造方法:");
c3.setAccessible(true);//设置暴力访问——不进行权限修饰符的验证
Object o3 = c3.newInstance("张三", 20);
}
2).获取成员属性:
A).批量获取:
1).public Field[] getFields():获取所有的"公有"成员属性。
2).public Field[] getDeclaredFields():获取所有的成员属性,包括公有、受保护、默认、私有的。
B).获取单个:
3).public Field getField(String fieldName):获取某个公有成员属性
4).public Field getDeclaredField(String fieldName):获取某个成员属性,可以是:公有、受保护、默认、私有
-设置属性的值:
Field-->set(Object targetObj , Object value)
-获取属性的值:
Field-->get(Object obj):
-设置暴力访问:
Field-->setAccessible(true)
创建Student类
public class Student {
public String name;
private boolean marry;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", marry=" + marry +
'}';
}
获取成员属性
import java.lang.reflect.Field;
/*
2).获取成员属性:
A).批量获取:
1).public Field[] getFields():获取所有的"公有"成员属性。
2).public Field[] getDeclaredFields():获取所有的成员属性,包括公有、受保护、默认、私有的。
B).获取单个:
3).public Field getField(String fieldName):获取某个公有成员属性
4).public Field getDeclaredField(String fieldName):获取某个成员属性,可以是:公有、受保护、默认、私有
*/
public class Demo05 {
public static void main(String[] args) throws Exception {
//1.获取Class
Class stuClass = Class.forName("com.itheima.demo05.Student");
System.out.println("获取公有、name属性:");
Field nameFiled = stuClass.getField("name");
System.out.println(nameFiled);
System.out.println("获取私有、marry属性:");
Field marryField = stuClass.getDeclaredField("marry");
System.out.println(marryField);
//2.创建Student对象
Object obj = stuClass.getConstructor().newInstance();
//3.为obj对象的name和marry属性赋值
nameFiled.set(obj, "张三");
System.out.println(obj);
//4.为obj对象的marry属性赋值为:true
marryField.setAccessible(true);//设置暴力访问
marryField.set(obj, true);
System.out.println(obj);
//获取
System.out.println("姓名:" + nameFiled.get(obj));
System.out.println("婚否:" + marryField.get(obj));
}
3).获取成员方法:
A).批量获取:
1).public Method[] getMethods():获取所有的"公有"成员方法。
2).public Method[] getDeclaredMethods():获取所有的成员方法,包括公有、受保护、默认、私有的。
B).获取单个:
3).public Method getMethod(String methodName,Class ... params):获取某个公有成员方法
4).public Method getDeclaredMethod(String methodName,Class... params):获取某个成员方法,可以是:公有、受保护、默认、私有
-调用方法:
Method-->invoke(Object targetObj,Object ... params)
注意:
如果没有访问权限,要先设置一下:暴力访问:
Method-->setAccessible(true)
创建学生类
public class Student {
public void show1(){
System.out.println("调用了公有、无参的show1()...");
}
private int show2(String name,int age){
System.out.println("调用了String、int参数的show2(),String = " + name + " int = " + age);
return 200;
}
获取成员方法
import java.lang.reflect.Method;
/*
3).获取成员方法:
A).批量获取:
1).public Method[] getMethods():获取所有的"公有"成员方法。
2).public Method[] getDeclaredMethods():获取所有的成员方法,包括公有、受保护、默认、私有的。
B).获取单个:
3).public Method getMethod(String methodName,Class ... params):获取某个公有成员方法
4).public Method getDeclaredMethod(String methodName,Class... params):获取某个成员方法,可以是:公有、受保护、默认、私有
-调用方法:
Method类-->invoke(Object targetObj , Object ... params)
*/
public class Demo06 {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class aClass = Class.forName("com.itheima.demo06.Student");
System.out.println("获取公有、无参的show1():");
Method show1Method = aClass.getMethod("show1");
System.out.println(show1Method);
System.out.println("获取私有、有参、有返回值的show2():");
Method show2Method = aClass.getDeclaredMethod("show2", String.class, int.class);
System.out.println(show2Method);
//2.创建对象
Object obj = aClass.getConstructor().newInstance();
//3.调用方法:
show1Method.invoke(obj);
show2Method.setAccessible(true);
Object result = show2Method.invoke(obj, "张三", 20);
System.out.println("返回值:" + result);
}
3. 反射的使用步骤
1)先获取类
获取Class对象的三种方式:
A).Object类的getClass()【已有对象了,通过对象获取】
B).任何数据类型(包括基本类型).class【对基本类型、类库中的类】 '
C).Class类的静态方法:forName(String className)【常用——对于我们自己的类】
以上三种方式,工作流程都一样:如果没有此类的Class对象,会先新建一个,然后返回;如果有,就直接获取并返回。
2)获取类的内部成员信息
3)创建这个类对象,并调用他的成员
反射demo案例
游戏类
public class MyGame {
public void run(){
System.out.println("沙漠地图...");
}
}
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
/*
反射的效果演示:
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//1.正常使用
/*Game game = new Game();
game.paoTu();*/
String className = getValue("className");//com.itheima.demo01.Game
String methodName = getValue("methodName");
//2.下面使用了反射
Class aClass = Class.forName(className);
Object obj = aClass.getConstructor().newInstance();
Method method = aClass.getMethod(methodName);
method.invoke(obj);
//以上四行代码,就相当于:
/*Game game = new Game();
game.paoTu();*/
}
//编写一个方法,获取配置文件中某个键的值
public static String getValue(String key) {
Properties pro = new Properties();
try (FileReader in = new FileReader("game.properties")) {
pro.load(in);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return pro.getProperty(key);
}
}
配置文件game.properties
className=com.itheima.demo21.student
filedName=name
filedAge=age