反射,是框架设计的灵魂。反射机制在框架设计中举足轻重,现在市面上绝大部分框架基本上都有使用Java的反射机制。例如加载数据库驱动的,用到的也是反射。Class.forName("com.mysql.jdbc.Driver");
反射机制,是Java进阶必不可少的知识。
反射机制
概念
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。也就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
反射就是把Java类中的各种成分映射成一个个的Java对象。
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
如图是类的正常加载过程:反射的原理在与class对象。
作用
- 可以在程序的运行过程中,动态操作这些对象
- 可以解耦,提高程序的可扩展性
常用类
Java.lang.Class;
Java.lang.reflect.Constructor;
Java.lang.reflect.Field;
Java.lang.reflect.Method;
Java.lang.reflect.Modifier;
类加载机制
ClassLoader用来加载Class文件到JVM,以供程序使用的。Java程序可以动态加载类定义,而这个动态加载的机制就是通过ClassLoader来实现的。
类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远远不限于类加载阶段。对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
ClassLoader介绍
1)启动类加载器/Bootstrap ClassLoader:前面已经介绍过,这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。
2)扩展类加载器/Extension ClassLoader:这个加载器由sun.misc.Launcher.ExtClassLoader实现,它负责加载<JAVA_HOME>\lib\ext目录中的类,开发者可以直接使用扩展类加载器。
3)应用程序类加载器/Application ClassLoader:这个类加载器由sun.misc.Launcher.AppClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(Class Path)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
举了例子验证一下吧
package reflectionDemo;
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
System.out.println("current loader: "+loader);
System.out.println("parent loader: "+loader.getParent());
}
}
控制台输出
current loader: sun.misc.Launcher$AppClassLoader@18b4aac2
parent loader: sun.misc.Launcher$ExtClassLoader@61bbe9ba
Java程序代码在计算机中运行经历的三个阶段:
反射实践
获取class信息
- Object.getClass()
- 类名.class:通过类名的属性class获取
- Class.forName(“全类名”):将字节码文件加载进内存,返回class对象
Student类
package reflectionDemo;
import lombok.*;
@Datapublic class Student { protected Integer age;
public String name; private String Phone; public String showName(String name){
System.out.println(name);
return name; } protected void showAge(){ System.out.println("age"); }
private String showPhone(String Phone){
System.out.println("Phone");
return Phone;
}
}
Demo:
package reflectionDemo;
public class ReflectionDemo {
public static void main(String[] args) { //第一种方式获取Class对象;这一new 产生一个Student对象,一个Class对象。 Student stu1 = new Student();
Class stuClass = stu1.getClass();//获取Class对象 System.out.println(stuClass.getName()); //第二种方式获取Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个 //第三种方式获取Class对象 try { Class stuClass3 = Class.forName("reflectionDemo.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名 System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
控制台输出
获取成员变量并调用
package reflectionDemo;
import java.lang.reflect.Field;
/*
* 获取成员变量并调用:
*
* 1.批量的
* 1).Field[] getFields():获取所有的"公有字段"
* 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
* 2.获取单个的:
* 1).public Field getField(String fieldName):获取某个"公有的"字段;
* 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
*
* 设置字段的值:
* Field --> public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2.value:要为字段设置的值;
*/
public class Demo_two {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("reflectionDemo.Student");
//2.获取字段
System.out.println("************获取所有公有的字段********************");
Field[] fieldArray = stuClass.getFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
fieldArray = stuClass.getDeclaredFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("*************获取公有字段**并调用***********************************");
Field f = stuClass.getField("name");
System.out.println(f);
//获取一个对象
//生成Student对象--> 等价Student stu = new Student();
Object obj = stuClass.getConstructor().newInstance();
//为字段设置值
f.set(obj, "软件质量保障"); //为Student对象中的name属性赋值--》stu.name = "软件质量保障"
//验证
Student stu = (Student)obj;
System.out.println("验证姓名:" + stu.name);
System.out.println("**************获取私有字段****并调用********************************");
f = stuClass.getDeclaredField("Phone");
System.out.println(f);
f.setAccessible(true);//解除私有限定
f.set(obj, "13240738783");
System.out.println("验证电话:" + stu);
}
}
控制台输出:
获取成员方法并调用
package reflectionDemo;
import java.lang.reflect.Method;
/*
* 获取成员方法并调用:
*
* 1.批量的:
* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
* public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
*
* 调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
):
*/
public class Demo_three {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("reflectionDemo.Student");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
stuClass.getMethods();
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取公有的showPhone()方法*******************");
Method m = stuClass.getMethod("showName", String.class);
System.out.println(m);
//实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
m.invoke(obj, "软件质量保障"); // 入参
System.out.println("***************获取私有的showName()方法******************");
m = stuClass.getDeclaredMethod("showPhone", String.class);
System.out.println(m);
m.setAccessible(true);//解除私有限定
Object result = m.invoke(obj, "13240282821");//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println("返回值:" + result);
}
控制台输出:
***************获取所有的”公有“方法*******************
public boolean reflectionDemo.Student.equals(java.lang.Object)
public java.lang.String reflectionDemo.Student.toString()
public int reflectionDemo.Student.hashCode()
public java.lang.String reflectionDemo.Student.getName()
public void reflectionDemo.Student.setName(java.lang.String)
public java.lang.String reflectionDemo.Student.showName(java.lang.String)
public java.lang.Integer reflectionDemo.Student.getAge()
public java.lang.String reflectionDemo.Student.getPhone()
public void reflectionDemo.Student.setAge(java.lang.Integer)
public void reflectionDemo.Student.setPhone(java.lang.String)
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 final void java.lang.Object.wait() throws java.lang.InterruptedException
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()
***************获取所有的方法,包括私有的*******************
public boolean reflectionDemo.Student.equals(java.lang.Object)
public java.lang.String reflectionDemo.Student.toString()
public int reflectionDemo.Student.hashCode()
public java.lang.String reflectionDemo.Student.getName()
public void reflectionDemo.Student.setName(java.lang.String)
public java.lang.String reflectionDemo.Student.showName(java.lang.String)
private java.lang.String reflectionDemo.Student.showPhone(java.lang.String)
protected void reflectionDemo.Student.showAge()
public java.lang.Integer reflectionDemo.Student.getAge()
public java.lang.String reflectionDemo.Student.getPhone()
public void reflectionDemo.Student.setAge(java.lang.Integer)
public void reflectionDemo.Student.setPhone(java.lang.String)
protected boolean reflectionDemo.Student.canEqual(java.lang.Object)
***************获取公有的showPhone()方法*******************
public java.lang.String reflectionDemo.Student.showName(java.lang.String)
软件质量保障
***************获取私有的showName()方法******************
private java.lang.String reflectionDemo.Student.showPhone(java.lang.String)
Phone
返回值:13240282821
附录:
解读ClassLoade