反射机制:指的是可以于运行时加载、探知、使用编译期间完全未知的类。
以我自身的理解来说,我们先以正射和反射为切入点。
正射:是我们有一个对象(物体),我们可以通过它去了解到他的属性、通过它调用它的方法。
反射:我们得到了这个对象(物体)的图纸,我们可以通过图纸去获得该图纸产生的对象(物体),或者不用通过对象直接获得属性和方法。因为图纸在我们面前,我们可以清楚的看到造出来的物体的零件(属性),知道造出来的物体的作用(方法)。
阐述完基本的概念后,先让我们来看下使用反射的一个基本条件(如何获得图纸,即Class对象)。
1、如何获取Class对象
public class Reflect {
public static void main(String[] args) throws ClassNotFoundException {
User u = new User("twk", 19, "male");
//第一种方法:使用.class直接获取。
Class people1 = User.class;
System.out.println(people1);
//第二种方法:使用该类的实例对象获取
Class people2 = u.getClass();
System.out.println(people2);
//第三种方法:使用类路径获取
Class people3 = Class.forName("com.twk.webServer.User");
System.out.println(people3);
}
}
这三种方法所取得的都是同一个Class对象。
2、如何获取Class对象中的属性、方法、构造器
(1)、获取类名
方法名称 | 作用 |
getName() | 获取包名+类名 |
getSimpleName() | 仅获取类名 |
示例:
public class ClassName {
public static void main(String[] args) {
Class c = User.class;
System.out.println(c.getName());
//com.twk.webServer.User
System.out.println(c.getSimpleName());
//User
}
}
(2)、获取属性
方法名 | 返回值 | 作用 |
getFields() | java.lang.reflect.Field[] | 只能获取公开的属性(public标识的属性) |
getDeclaredFields() | java.lang.reflect.Field[] | 获取类中所有声明的属性,无论是否公开 |
getDeclaredField(属性名) | java.lang.reflect.Field | 获取指定名称的属性对象 |
示例:
我们在下面的程序中故意设置了一个public属性和两个private属性来验证getFields()和getDeclaredFields()方法的不同效果。
public class User {
public String name;
private int age;
private String sex;
}
public class ClassField {
public static void main(String[] args) throws NoSuchFieldException {
Class c = User.class;
Field[] fs = c.getFields();
for (Field temp : fs) {
System.out.print(temp.getName() + "--");
}
//name--
System.out.println();
Field[] dfs = c.getDeclaredFields();
for (Field temp : dfs) {
System.out.print(temp.getName() + "--");
}
//name--age--sex--
System.out.println();
Field f = c.getDeclaredField("name");
System.out.println(f.getName());
//name
}
}
(3)、获取方法
方法名 | 返回值 | 作用 |
getDeclaredMethods() | java.lang.reflect.Method[] | 获取声明公开的所有方法 |
getDeclaredMethod(方法名,参数类型.class...) | java.lang.reflect.Method | 通过方法名称和参数类型获取指定方法 |
示例:
在下列代码中我们专门将getSex和setSex方法设置private类型。结果getDeclaredMethods()方法找不到该方法,所以可知该方法无法找到private标识的方法。
User类:
public class User {
public String name;
private int age;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
private String getSex() {
return sex;
}
private void setSex(String sex) {
this.sex = sex;
}
public User() {
}
public User(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
public class ClassMethor {
public static void main(String[] args) throws NoSuchMethodException {
Class c = User.class;
Method[] ms = c.getMethods();
for (Method temp : ms) {
System.out.print(temp.getName() + "--");
}
// getName--setName--setAge--getAge--wait--wait--wait--equals--toString--hashCode--getClass--notify--notifyAll--
System.out.println();
Method m = c.getDeclaredMethod("setName", String.class);
System.out.println(m.getName());
//setName
}
}
(4)、获取构造器
方法名 | 返回值 | 作用 |
getConstructors() | java.lang.reflect.Constructor<?>[] | 获取所有构造器 |
getDeclaredConstructor(参数类型.class...) | java.lang.reflect.Constructor<?> | 通过参数类型获取指定构造器 |
示例:
public class ClassConstructor {
public static void main(String[] args) throws NoSuchMethodException {
Class c = User.class;
Constructor[] cs = c.getConstructors();
for (Constructor temp : cs) {
System.out.println(temp);
}
/*
* public com.twk.webServer.User(java.lang.String,int,java.lang.String)
public com.twk.webServer.User()
* */
Constructor c1 = c.getConstructor();
System.out.println(c1);
//public com.twk.webServer.User()
Constructor c2 = c.getConstructor(String.class, int.class, String.class);
System.out.println(c2);
//public com.twk.webServer.User(java.lang.String,int,java.lang.String)
}
}
了解完反射的基础之后,让我进一步使用Class对象(图纸)来实例化出对应的对象。
(5)、使用反射机制实例化对象
方法名 | 返回值 | 作用 |
newInstance() | T | 返回对应Class对象的实例 |
getDeclaredConstructor(参数类型.class...) | java.lang.reflect.Constructor<T> | 返回对应参数的构造器对象 |
示例:
public class ClassObject {
public static void main(String[] args) throws Exception {
Class c = User.class;
User u = (User) c.newInstance();
System.out.println(u);
//com.twk.webServer.User@7a79be86
Constructor<User> con = c.getDeclaredConstructor(String.class, int.class, String.class);
User u2 = con.newInstance("zhangsan", 20, "男");
System.out.println(u2.getName() + "--" + u2.getAge());
//zhangsan--20
}
}
3、使用反射机制
(1)、使用反射机制调用对象的某一个方法
public class ClassInvoke {
public static void main(String[] args) throws Exception {
//获取User类的Class对象
Class c = User.class;
//实例化一个User对象,直接使用newInstance方法表示调用的是无参构造器
User u = (User) c.newInstance();
//取出User的Class对象中的setName方法
Method m1 = c.getDeclaredMethod("setName", String.class);
//唤醒setName方法,传入对应参数
m1.invoke(u, "lisi");
System.out.println(u.getName());
//lisi
}
}
(2)、使用反射机制操作属性
public class ClassInvoke2 {
public static void main(String[] args) throws Exception {
Class c = User.class;
User u = (User) c.newInstance();
//获取Class对象中名为name的属性
Field f = c.getDeclaredField("name");
//设置u对象中f属性对象所对应属性的值。这里需要明确的是f是从Class对象获得的,
//所以是不知道u对象的,因此修改f所对应的属性时,不仅要告知修改为什么值,还需要告知修改谁?
f.set(u, "zhangsan");
System.out.println(u.getName());
//zhangsan
System.out.println(f.get(u));
//zhangsan
}
}
了解完反射机制的大致内容后,大家可以思考如何去利用反射机制制作一些有趣的小demo。
在此,我将利用反射机制制作一个利用控制台来对一个对象进行操作的demo:
短时间做出来的demo主要用于回顾反射机制,里面还有不少小bug。如:不可以操作多个参数的方法、输入带参数的方法但是没输入参数时只会报错而不是叫大家重新输入。
User.class
public class User {
public String name;
private int age;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public User() {
}
public User(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
ReflectDemo.class
package com.twk.webServer;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, IllegalAccessException {
System.out.println("是否需要new 一个User对象?:(Y or N)");
Scanner scan = new Scanner(System.in);
String isAccess = scan.nextLine();
if (isAccess.equals("Y")){
Class c = User.class;
User u = new User();
boolean flag = true;
while(flag){
StringBuilder sb = new StringBuilder();
sb.append("选择你需要执行的业务:\n");
sb.append("1、查询对象所有属性\n");
sb.append("2、查询对象所有方法\n");
sb.append("直接输入字符+空格+对应的参数类型可执行对应的方法\n");
sb.append("输入\"quit\"即可退出程序\n");
System.out.println(sb.toString());
Scanner reader = new Scanner(System.in);
String s = reader.nextLine();
switch (s){
case "1":
Field[] fields = c.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i].getName());;
}
break;
case "2":
Method[] methods = c.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i].getName());
}
break;
case "quit":
flag = false;
break;
default:
String[] s1 = s.split(" ");
boolean ishaveParameter = true;
Class parameterType = null;
Method m;
if(s1.length == 1){
ishaveParameter = false;
}else{
for(int i = 1; i < s1.length; i++){
if(isNumeric(s1[i])){
parameterType = int.class;
}else{
parameterType = String.class;
}
}
}
if(ishaveParameter){
m = c.getDeclaredMethod(s1[0], parameterType);
m.invoke(u, parameterType == int.class ? Integer.parseInt(s1[1]) : s1[1]);
}else {
m = c.getDeclaredMethod(s1[0]);
String res = (String) m.invoke(u);
System.out.println(res);
}
break;
}
}
System.out.println("下次再见");
}else {
System.out.println("下次再见");
}
}
public static boolean isNumeric(String str){
for (int i = str.length();--i>=0;){
if (!Character.isDigit(str.charAt(i))){
return false;
}
}
return true;
}
}
结果图: