参考视频链接
反射黑马教程
反射允许对封装类的字段,方法和构造函数的信息进行编程访问
它可以将一个类里面的方法和成员变量获取出来
反射是通过 class 文件中获取这字段,构造方法和成员方法,然后在从这三个当中进行解剖,获取更多的信息。
获取 class 对象的三种方式
Class cl1 = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");
System.out.println(cl1);
// 第二种方法
Class cl2 = Student.class;
System.out.println(cl2);
// 第三种方法
Class cl3 = new Student().getClass();
System.out.println(cl3);
System.out.println(cl1 == cl2);
System.out.println(cl2 == cl3);
总结
第一种方式是最为常用的, 第二种方式我们一般是用来当作参数进行传递的, 第三种方式只有当我们已经有了这个类的对象之后, 才可以使用.
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//1.获得整体(class字节码文件对象)
Class clazz = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");
//2.获取构造方法对象
//获取所有构造方法(public)
Constructor[] constructors1 = clazz.getConstructors();
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
System.out.println("=======================");
//获取所有构造(带私有的)
Constructor[] constructors2 = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors2) {
System.out.println(constructor);
}
System.out.println("=======================");
//获取指定的空参构造
Constructor con1 = clazz.getConstructor();
System.out.println(con1);
Constructor con2 = clazz.getConstructor(String.class,int.class);
System.out.println(con2);
System.out.println("=======================");
//获取指定的构造(所有构造都可以获取到,包括public包括private)
Constructor con3 = clazz.getDeclaredConstructor();
System.out.println(con3);
//了解 System.out.println(con3 == con1);
//每一次获取构造方法对象的时候,都会新new一个。
Constructor con4 = clazz.getDeclaredConstructor(String.class);
System.out.println(con4);
}
}
规则:
get表示获取
Declared表示私有
最后的s表示所有,复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
常用方法
方法名 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿 public 的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
public class ReflectDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//获取成员变量对象
//1.获取class对象
Class clazz = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");
//2.获取成员变量的对象(Field对象)只能获取public修饰的
Field[] fields1 = clazz.getFields();
for (Field field : fields1) {
System.out.println(field);
}
System.out.println("===============================");
//获取成员变量的对象(public + private)
Field[] fields2 = clazz.getDeclaredFields();
for (Field field : fields2) {
System.out.println(field);
}
System.out.println("===============================");
//获得单个成员变量对象
//如果获取的属性是不存在的,那么会报异常
//Field field3 = clazz.getField("aaa");
//System.out.println(field3);//NoSuchFieldException
Field field4 = clazz.getField("gender");
System.out.println(field4);
System.out.println("===============================");
//获取单个成员变量(私有)
Field field5 = clazz.getDeclaredField("name");
System.out.println(field5);
}
}
方法 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 获取值 |
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
Student s = new Student("zhangsan",23,"广州");
Student ss = new Student("lisi",24,"北京");
//需求:
//利用反射获取成员变量并获取值和修改值
//1.获取class对象
Class clazz = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");
//2.获取name成员变量
//field就表示name这个属性的对象
Field field = clazz.getDeclaredField("name");
//临时修饰他的访问权限
field.setAccessible(true);
//3.设置(修改)name的值
//参数一:表示要修改哪个对象的name?
//参数二:表示要修改为多少?
field.set(s,"wangwu");
//3.获取name的值
//表示我要获取这个对象的name的值
String result = (String)field.get(s);
//4.打印结果
System.out.println(result);
System.out.println(s);
System.out.println(ss);
}
常用方法
方法名 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
//1.获取class对象
Class<?> clazz = Class.forName("Metamorphosis.day6.Reflect.GetClass1.Student");
//2.获取方法
//getMethods可以获取父类中public修饰的方法
Method[] methods1 = clazz.getMethods();
for (Method method : methods1) {
System.out.println(method);
}
System.out.println("===========================");
//获取所有的方法(包含私有)
//但是只能获取自己类中的方法
Method[] methods2 = clazz.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println("===========================");
//获取指定的方法(空参)
Method method3 = clazz.getMethod("sleep");
System.out.println(method3);
Method method4 = clazz.getMethod("eat",String.class);
System.out.println(method4);
//获取指定的私有方法
Method method5 = clazz.getDeclaredMethod("playGame");
System.out.println(method5);
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//1.获取字节码文件对象
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
//2.获取一个对象
//需要用这个对象去调用方法
Student s = new Student();
//3.获取一个指定的方法
//参数一:方法名
//参数二:参数列表,如果没有可以不写
Method eatMethod = clazz.getMethod("eat",String.class);
//运行
//参数一:表示方法的调用对象
//参数二:方法在运行时需要的实际参数
//注意点:如果方法有返回值,那么需要接收invoke的结果
//如果方法没有返回值,则不需要接收
String result = (String) eatMethod.invoke(s, "重庆小面");
System.out.println(result);
}
平常我们修改代码的时候我们一般是进行侵入式修改, 也就是直接修改源代码
当我们既不想修改修改源代码, 可是又想要增加额外的功能, 这种情况下我们就可以使用这个动态代理.
程序为什么需要代理, 代理它长什么样
Java. Long. Reflect. Proxy 类中提供了为对象产生代理对象的方法
public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,InnovationHandler h
明星对象 (我省略了 setter 和 getter 方法)
public class Star implements daili {
private String name;
@Override
public String sing(String name) {
System.out.println(this.name + " 正在演唱 " + name);
return "谢谢";
}
@Override
public String dance() {
System.out.println(this.name + " 正在跳舞");
return "谢谢";
}
}
代理 (这是一个接口, 包含了明星对象的几个功能代码)
public interface daili {
public abstract String sing(String name);
public abstract String dance();
}
Proxy 类
public class ProxyTest {
public static daili createProxy(Star star) {
daili starDaili = (daili) Proxy.newProxyInstance(
ProxyTest.class.getClassLoader(), // 参数一:指定用哪个类加载器,去加载生成的类
new Class[] { daili.class }, // 参数二:指定接口,这些接口用于指定代理是什么样的,也就是有哪些方法
// 参数三,用于指定这个代理做什么事情.
new InvocationHandler() {
/**
* 参数一: 代理的对象
* 参数二: 要运行的方法sing
* 参数三: 调用sing方法的时候传递的实参
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("sing".equals(method.getName())) {
System.out.println("准备场地,收钱可以开始唱歌了");
} else if ("dance".equals(method.getName())) {
System.out.println("准备场地,收钱可以开始跳舞了");
}
// 之后这个代理,去找这个明星类中的唱歌和跳舞的方法,这里需要利用反射的知识
method.invoke(star, args);
return "谢谢";
}
});
return starDaili;
}
}
测试代码
public class Test {
/**
* 如果想要让这个明星唱歌:
* 我们首先要找到这个代理
* ProxyTest.createProxy(Star star);
* 然后通过这个代理去执行这个唱歌的方法
* 代理对象.唱歌("鸡你太美") //也就是我们的第三个参数
*/
public static void main(String[] args) {
Star star = new Star("坤哥");
daili createProxy = ProxyTest.createProxy(star);
String result = createProxy.sing("鸡你太美");
System.out.println(result);
createProxy.dance();
}
}