反射的定义:加载类,并允许以编程的方式解剖类中的各种成分(成员方法,方法,构造器)。反射具体就是获取类的信息,并操作它们。
反射的主要作用:设计框架
1、获取类的字节码,Class对象
2、获取类的构造器:Constructor对象
3、获取类的成员变量:Field对象
4、获取类的成员方法:Method对象
获取类具有三种方法,一种是需要获取的类名.class,一种是该类的队伍.getClass(),还有一种是获取类本身:Class.forName("类的全类名"),即括号内输入包在内的类的类名
示例
package ReflectDemo;
public class ReflectDemo1 {
public static void main(String[] args) throws Exception{
//目标:掌握反射的第一步操作:获取类的Class对象
//1、获取类本身:类名.class
Class c1 = Student.class;
System.out.println(c1);
//2、获取类本身:对象.getClass()
Student s = new Student();
Class c2 = s.getClass();
System.out.println(c2);
//3、通过Class.forName()
Class c3 = Class.forName("ReflectDemo.Student");
System.out.println(c3);
System.out.println(c1==c2&&c2==c3);
}
}
获取类的构造器对象,并对其进行操作。
package ReflectDemo;
public class Dog {
private String name;
private int age;
private String hobby;
private Dog() {
System.out.println("无参构造器");
}
private Dog(String name) {
this.name = name;
System.out.println("一个参数的有参构造器");
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
System.out.println("两个参数的有参构造器");
}
private String eat(String food) {
System.out.println("狗吃"+food);
return "狗说:谢谢!谢谢!";
}
public void eat(){
System.out.println("狗吃东西!");
}
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 getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
//2、获取类的构造器对象,并对其进行操作
@Test
public void getConstructor() throws Exception {
//获取类
Class c1 = Dog.class;
//获取全部构造器对象
Constructor[] con = c1.getDeclaredConstructors();
for (Constructor c : con) {
System.out.println(c.getName()+"("+c.getParameterCount()+")");
}
System.out.println("---------------------------------------------------------");
//获取单个构造器
Constructor con1 = c1.getDeclaredConstructor();//获取无参构造器
System.out.println(con1.getName()+"("+con1.getParameterCount()+")");
Constructor con2 = c1.getDeclaredConstructor(String.class,int.class);//获取2个参数的有参构造器
System.out.println(con2.getName()+"("+con2.getParameterCount()+")");
//获取构造器的作用依然是创建对象
//暴力反射:暴力反射可以访问私有的构造器,方法,属性
con1.setAccessible(true);//临时绕过访问权限
Dog d1=(Dog)con1.newInstance();//创建对象
System.out.println(d1);
Dog d2 = (Dog)con2.newInstance("小黑", 3);
System.out.println(d2);
}
获取类的成员变量,并进行操作
//3、获取类的成员变量,并对其进行操作
@Test
public void getFieldInfo() throws Exception {
//获取类
Class c1 = Dog.class;
//获取成员变量对象
Field[] fields = c1.getDeclaredFields();
for (Field f : fields) {
System.out.println(f.getName()+"("+f.getType().getName()+")");
}
//获取单个成员变量对象
Field field = c1.getDeclaredField("hobby");
System.out.println(field.getName()+"("+field.getType().getName()+")");
//获取成员变量的目的依然是取值和赋值
Dog d1 = new Dog("小黑", 3);
field.setAccessible(true);//临时绕过访问权限
field.set(d1,"吃吃吃");
System.out.println(d1);
String hobby = (String) field.get(d1);//与d1.getHobby()效果一样
}
获取类的成员方法,并进行操作
//4、获取类的成员方法,并进行操作
@Test
public void getMethodInfo() throws Exception {
//获取类
Class c1 = Dog.class;
//获取成员方法对象
Method[] methods = c1.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m.getName()+"("+m.getParameterCount()+")");
}
//获取单个成员方法对象
Method method = c1.getDeclaredMethod("eat", String.class);//获取有参方法
Method method1 = c1.getDeclaredMethod("eat");//获取无参方法
System.out.println(method.getName()+"("+method.getParameterCount()+")");
//获取成员方法的作用依然是调用方法
Dog d1 = new Dog("小黑", 3);
method.setAccessible(true);//临时绕过访问权限
Object re = method.invoke(d1, "骨头");//相当于d1.eat("骨头")
Object re1 = method1.invoke(d1);
System.out.println(re);//唤醒对象method的eat带String参数方法执行,相当于d1.eat("骨头")
System.out.println(re1);
}
获取这些对象的方法类似,以Field为例,若需要获得单个成员变量就使用对象名.getDeclaredField(参数)方法,若需要获取全部的成员变量则需要用对象名.getDeclaredFields()方法并用成员变量数组来接收。
当需要对取得的私有成员变量或方法或构造器进行使用时,需要用对象名.setAccessible(true)方法临时绕过访问权限,使其可以对私有对象进行操作。
反射还可以绕过泛型的约束
package ReflectDemo;
import java.lang.reflect.Method;
import java.util.ArrayList;
public class ReflectDemo3 {
public static void main(String[] args) throws Exception{
//目标:反射的基本作用
//1、类的全部成份的获取
//2、可以破坏封装性
//3、可以绕过泛型的约束
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
//list.add(9.9);
Class c1 = list.getClass();
//获取ArrayList类中的add方法
Method add = c1.getMethod("add", Object.class);
//触发List集合对象的add方法执行
add.invoke(list, 9.9);
add.invoke(list, true);
System.out.println(list);
//反射最重要的用途:适合做Java的框架,基本上,主流的框架都会基于反射设计出一些通用的功能
}
}
ArrayList集合被泛型约束只能够存储字符串,如果为小数和boolean数据类型则报错,但如果使用反射(获取类对象,获取方法,用该方法操作)进行操作却可以加入数字和boolean数据类型,说明反射可以绕过泛型的约束。
项目实战,用反射模拟一个框架,具有把对象的成员变量字段名和对应的值保存到文件中去的能力。
package ReflectDemo;
public class Dog {
private String name;
private int age;
private String hobby;
private Dog() {
System.out.println("无参构造器");
}
private Dog(String name) {
this.name = name;
System.out.println("一个参数的有参构造器");
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
System.out.println("两个参数的有参构造器");
}
private String eat(String food) {
System.out.println("狗吃"+food);
return "狗说:谢谢!谢谢!";
}
public void eat(){
System.out.println("狗吃东西!");
}
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 getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
package ReflectDemo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private String name;
private int age;
private String hobby;
private double salary;
private String className;
private char sex;
private String phone;
}
package ReflectDemo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
private String hobby;
}
package ReflectDemo;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
public class SaveObjectFrameWork {
//保存任意对象的静态方法
public static void saveObject(Object obj) throws Exception{
PrintStream ps = new PrintStream(new FileOutputStream("day06 - junit - reflect - annotation - proxy\\src\\ReflectDemo\\message.txt",true));
//obj 可能是学生,老师,狗
//只有反射可以知道对象有多少个字段
//1、获取Class对象
Class c1 = obj.getClass();
String simpleName = c1.getSimpleName();
ps.println("============"+simpleName+"===============");
//2、获取所有字段
Field[] fields = c1.getDeclaredFields();
//3、遍历字段
for(Field f:fields){
//4、获取字段值
//4.1、获取字段名
f.setAccessible(true);
String fieldName = f.getName();
//4.2、获取字段值
Object fieldValue = f.get(obj)+"";
//5、打印到文件中去
ps.println(fieldName+"="+fieldValue);
}
ps.close();
}
}
package ReflectDemo;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception{
//搞清楚反射的应用:做框架的通用技术
//设计一个框架:该框架可以把对象的成员变量字段名和对应的值,保存到文件中去
Dog dog = new Dog("旺财", 3);
//创建学生对象
Student stu = new Student("小明",17,"足球");
//创建老师对象
Teacher t = new Teacher("小王", 18, "java,前端", 5000, "软件工程", '男', "12345678901");
SaveObjectFrameWork.saveObject(dog);
SaveObjectFrameWork.saveObject(stu);
SaveObjectFrameWork.saveObject(t);
}
}
上述使用了反射,打印流知识点,SaveObjectFrameWork类作为工具类,其他三个类是用于示范虽然成员变量数不一样但反射可以把三个不同的类的全部对象都存到文件里。