1. 反射
1.1 反射的概述:
专业的解释(了解一下):
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意属性和方法;
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
通俗的理解:(掌握)
1.2 学习反射到底学什么?
反射都是从class字节码文件中获取的内容。
- 如何获取class字节码文件的对象
- 利用反射如何获取构造方法(创建对象)
- 利用反射如何获取成员变量(赋值,获取值)
- 利用反射如何获取成员方法(运行)
1.3 获取字节码文件对象的三种方式
- Class这个类里面的静态方法forName(“全类名”)(最常用)
- 通过class属性获取
- 通过对象获取字节码文件对象
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Class clazz1 = Class.forName("com.itheima.reflectdemo.Student");
Class clazz2 = Student.class;
System.out.println(clazz1 == clazz2);
Student s = new Student(); Class clazz3 = s.getClass(); System.out.println(clazz1 == clazz2); System.out.println(clazz2 == clazz3);
|
1.4 字节码文件和字节码文件对象
java文件:就是我们自己编写的java代码。
字节码文件:就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)
字节码文件对象:当class文件加载到内存之后,虚拟机自动创建出来的对象。
这个对象里面至少包含了:构造方法,成员变量,成员方法。
而我们的反射获取的是什么?字节码文件对象,这个对象在内存中是唯一的。
1.5 获取构造方法
规则:
get表示获取
Declared表示私有
最后的s表示所有,复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名 |
说明 |
Constructor<?>[] getConstructors() |
获得所有的构造(只能public修饰) |
Constructor<?>[] getDeclaredConstructors() |
获得所有的构造(包含private修饰) |
Constructor getConstructor(Class<?>… parameterTypes) |
获取指定构造(只能public修饰) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) |
获取指定构造(包含private修饰) |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class ReflectDemo2 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class clazz = Class.forName("com.itheima.reflectdemo.Student");
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("======================="); Constructor con3 = clazz.getDeclaredConstructor(); System.out.println(con3);
Constructor con4 = clazz.getDeclaredConstructor(String.class); System.out.println(con4); } }
|
1.6 获取构造方法并创建对象
涉及到的方法:newInstance
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| public class Student { private String name;
private int age;
public Student() {
}
public Student(String name) { this.name = name; }
private Student(String name, int age) { this.name = name; this.age = age; }
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 toString() { return "Student{name = " + name + ", age = " + age + "}"; } }
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
Constructor con = clazz.getConstructor();
Student stu = (Student) con.newInstance(); System.out.println(stu);
System.out.println("=============================================");
Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student");
Constructor con = clazz.getDeclaredConstructor(String.class, int.class);
con.setAccessible(true);
Student stu = (Student) con.newInstance("zhangsan", 23); System.out.println(stu);
|
1.7 获取成员变量
规则:
get表示获取
Declared表示私有
最后的s表示所有,复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名:
方法名 |
说明 |
Field[] getFields() |
返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() |
返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) |
返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) |
返回单个成员变量对象,存在就能拿到 |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
| public class ReflectDemo4 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
Field[] fields1 = clazz.getFields(); for (Field field : fields1) { System.out.println(field); }
System.out.println("===============================");
Field[] fields2 = clazz.getDeclaredFields(); for (Field field : fields2) { System.out.println(field); }
System.out.println("===============================");
Field field4 = clazz.getField("gender"); System.out.println(field4);
System.out.println("==============================="); Field field5 = clazz.getDeclaredField("name"); System.out.println(field5);
} }
public class Student { private String name;
private int age;
public String gender;
public String address;
public Student() { }
public Student(String name, int age, String address) { this.name = name; this.age = age; this.address = address; }
public Student(String name, int age, String gender, String address) { this.name = name; this.age = age; this.gender = gender; this.address = address; }
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 getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public String toString() { return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}"; } }
|
1.8 获取成员变量并获取值和修改值
方法 |
说明 |
void set(Object obj, Object value) |
赋值 |
Object get(Object obj) |
获取值 |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
| public class ReflectDemo5 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException { Student s = new Student("zhangsan",23,"广州"); Student ss = new Student("lisi",24,"北京");
Class clazz = Class.forName("com.itheima.reflectdemo.Student");
Field field = clazz.getDeclaredField("name"); field.setAccessible(true);
field.set(s,"wangwu");
String result = (String)field.get(s);
System.out.println(result);
System.out.println(s); System.out.println(ss);
} }
public class Student { private String name; private int age; public String gender; public String address;
public Student() { }
public Student(String name, int age, String address) { this.name = name; this.age = age; this.address = address; }
public Student(String name, int age, String gender, String address) { this.name = name; this.age = age; this.gender = gender; this.address = address; }
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 getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public String toString() { return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", address = " + address + "}"; } }
|
1.9 获取成员方法
规则:
get表示获取
Declared表示私有
最后的s表示所有,复数形式
如果当前获取到的是私有的,必须要临时修改访问权限,否则无法使用
方法名 |
说明 |
Method[] getMethods() |
返回所有成员方法对象的数组(只能拿public的) |
Method[] getDeclaredMethods() |
返回所有成员方法对象的数组,存在就能拿到 |
Method getMethod(String name, Class<?>… parameterTypes) |
返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) |
返回单个成员方法对象,存在就能拿到 |
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class ReflectDemo6 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class<?> clazz = Class.forName("com.itheima.reflectdemo.Student");
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); } }
|
1.10 获取成员方法并运行
方法
Object invoke(Object obj, Object… args) :运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
| package com.itheima.a02reflectdemo1;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class ReflectDemo6 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class clazz = Class.forName("com.itheima.a02reflectdemo1.Student"); Student s = new Student(); Method eatMethod = clazz.getMethod("eat",String.class); String result = (String) eatMethod.invoke(s, "重庆小面"); System.out.println(result);
} }
public class Student { private String name; private int age; public String gender; public String address;
public Student() {
}
public Student(String name) { this.name = name; }
private Student(String name, int age) { this.name = name; this.age = age; }
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 toString() { return "Student{name = " + name + ", age = " + age + "}"; }
private void study(){ System.out.println("学生在学习"); }
private void sleep(){ System.out.println("学生在睡觉"); }
public String eat(String something){ System.out.println("学生在吃" + something); return "学生已经吃完了,非常happy"; } }
|
面试题:
你觉得反射好不好?好,有两个方向
第一个方向:无视修饰符访问类中的内容。但是这种操作在开发中一般不用,都是框架底层来用的。
第二个方向:反射可以跟配置文件结合起来使用,动态的创建对象,动态的调用方法。
1.11 练习泛型擦除(掌握概念,了解代码)
理解:(掌握)
集合中的泛型只在java文件中存在,当编译成class文件之后,就没有泛型了。
代码示例:(了解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.itheima.reflectdemo;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList;
public class ReflectDemo8 { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ArrayList<Integer> list = new ArrayList<>(); list.add(123);
Class clazz = list.getClass();
Method method = clazz.getMethod("add", Object.class);
method.invoke(list,"aaa");
System.out.println(list); } }
|
1.12 练习:修改字符串的内容(掌握概念,了解代码)
在这个练习中,我需要你掌握的是字符串不能修改的真正原因。
字符串,在底层是一个byte类型的字节数组,名字叫做value
1
| private final byte[] value;
|
真正不能被修改的原因:final和private
final修饰value表示value记录的地址值不能修改。
private修饰value而且没有对外提供getvalue和setvalue的方法。所以,在外界不能获取或修改value记录的地址值。
如果要强行修改可以用反射:
代码示例:(了解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| String s = "abc"; String ss = "abc";
Class clazz = s.getClass();
Field field = clazz.getDeclaredField("value");
field.setAccessible(true);
byte[] bytes = (byte[]) field.get(s); bytes[0] = 100;
System.out.println(s); System.out.println(ss);
|
1.13 练习,反射和配置文件结合动态获取的练习(重点)
需求: 利用反射根据文件中的不同类名和方法名,创建不同的对象并调用方法。
分析:
①通过Properties加载配置文件
②得到类名和方法名
③通过类名反射得到Class对象
④通过Class对象创建一个对象
⑤通过Class对象得到方法
⑥调用方法
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| public class ReflectDemo9 { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Properties prop = new Properties(); FileInputStream fis = new FileInputStream("day14-code\\prop.properties"); prop.load(fis); fis.close(); System.out.println(prop);
String classname = prop.get("classname") + ""; String methodname = prop.get("methodname") + "";
Class clazz = Class.forName(classname);
Constructor con = clazz.getDeclaredConstructor(); con.setAccessible(true); Object o = con.newInstance(); System.out.println(o);
Method method = clazz.getDeclaredMethod(methodname); method.setAccessible(true);
method.invoke(o);
} }
配置文件中的信息: classname=com.itheima.a02reflectdemo1.Student methodname=sleep
|
1.14 利用发射保存对象中的信息(重点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class MyReflectDemo { public static void main(String[] args) throws IllegalAccessException, IOException {
Student s = new Student("小A",23,'女',167.5,"睡觉"); Teacher t = new Teacher("播妞",10000); saveObject(s); }
public static void saveObject(Object obj) throws IllegalAccessException, IOException { Class clazz = obj.getClass(); BufferedWriter bw = new BufferedWriter(new FileWriter("myreflect\\a.txt")); Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); String name = field.getName(); Object value = field.get(obj); bw.write(name + "=" + value); bw.newLine(); }
bw.close();
} }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| public class Student { private String name; private int age; private char gender; private double height; private String hobby;
public Student() { }
public Student(String name, int age, char gender, double height, String hobby) { this.name = name; this.age = age; this.gender = gender; this.height = height; this.hobby = hobby; }
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 char getGender() { return gender; }
public void setGender(char gender) { this.gender = gender; }
public double getHeight() { return height; }
public void setHeight(double height) { this.height = height; }
public String getHobby() { return hobby; }
public void setHobby(String hobby) { this.hobby = hobby; }
public String toString() { return "Student{name = " + name + ", age = " + age + ", gender = " + gender + ", height = " + height + ", hobby = " + hobby + "}"; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class Teacher { private String name; private double salary;
public Teacher() { }
public Teacher(String name, double salary) { this.name = name; this.salary = salary; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getSalary() { return salary; }
public void setSalary(double salary) { this.salary = salary; }
public String toString() { return "Teacher{name = " + name + ", salary = " + salary + "}"; } }
|
2. 动态代理
2.1 好处:
无侵入式的给方法增强功能
2.2 动态代理三要素:
1,真正干活的对象
2,代理对象
3,利用代理调用方法
切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。
2.3 代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Test { public static void main(String[] args) {
BigStar bigStar = new BigStar("鸡哥"); Star proxy = ProxyUtil.createProxy(bigStar);
String result = proxy.sing("只因你太美"); System.out.println(result); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
Star star = (Star) Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() { @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("准备场地,收钱"); } return method.invoke(bigStar,args); } } ); return star; } }
|
1 2 3 4 5 6 7
| public interface Star { public abstract String sing(String name); public abstract void dance(); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class BigStar implements Star { private String name;
public BigStar() { }
public BigStar(String name) { this.name = name; }
@Override public String sing(String name){ System.out.println(this.name + "正在唱" + name); return "谢谢"; }
@Override public void dance(){ System.out.println(this.name + "正在跳舞"); }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String toString() { return "BigStar{name = " + name + "}"; } }
|
2.4 额外扩展
动态代理,还可以拦截方法
比如:
在这个故事中,经济人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。
但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
public class ProxyUtil { public static Star createProxy(BigStar bigStar){ public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) Star star = (Star) Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(), new Class[]{Star.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("cleanWC".equals(method.getName())){ System.out.println("拦截,不调用大明星的方法"); return null; } return method.invoke(bigStar,args); } } ); return star; } }
|
2.5 动态代理的练习
对add方法进行增强,对remove方法进行拦截,对其他方法不拦截也不增强
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| public class MyProxyDemo1 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>();
List proxyList = (List) Proxy.newProxyInstance( MyProxyDemo1.class.getClassLoader(), new Class[]{List.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("add")) { long start = System.currentTimeMillis(); method.invoke(list, args); long end = System.currentTimeMillis(); System.out.println("耗时时间:" + (end - start)); return true; }else if(method.getName().equals("remove") && args[0] instanceof Integer){ System.out.println("拦截了按照索引删除的方法"); return null; }else if(method.getName().equals("remove")){ System.out.println("拦截了按照对象删除的方法"); return false; }else{ method.invoke(list,args); return null; } } } );
proxyList.add("aaa"); proxyList.add("bbb"); proxyList.add("ccc"); proxyList.add("ddd");
proxyList.remove(0); proxyList.remove("aaa");
System.out.println(list); } }
|