Java反射的学习
参考链接:https://blog.csdn.net/weixin_60475929/article/details/122653101
1.实例化Class类对象的四种方法
Java获得类对象的三种方式
@Test
public void testString() throws ClassNotFoundException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
// 1.通过类名.class获取类对象
Class<com.bilibili.juc.Student> studentClass = com.bilibili.juc.Student.class;
// 输出:studentClass:class com.bilibili.juc.Student
System.out.println("studentClass:" + studentClass);
// 2.通过实例对象.getClass()获取类对象
com.bilibili.juc.Student student = new com.bilibili.juc.Student();
Class<? extends com.bilibili.juc.Student> aClass = student.getClass();
// 输出:aClass:class com.bilibili.juc.Student
System.out.println("aClass:" + aClass);
// 3.通过Class.forName("类的全限定类名")获取类对象
Class<?> classForName = Class.forName("com.bilibili.juc.Student");
// 输出:classForName:class com.bilibili.juc.Student
System.out.println("classForName:" + classForName);
}
// 反射的第一步就是先得到编译后的Class对象,然后就可以得到Class的全部成分
// 获取到类对象后,就可以获取到类对应的构造方法、属性以及方法,用于创建对象或方法调用
哪些类型可以有Class类对象
2.实体类Student
package com.bilibili.juc;
public class Student implements Cloneable{
private Double weight;
private Integer pkid;
private String name;
public Student() {
}
public Student(Double weight){
this.weight = weight;
}
private Student(Double weight, Integer pkid, String name) {
this.weight = weight;
this.pkid = pkid;
this.name = name;
}
public void study(){
System.out.println("我是学生,我要学习");
}
private void eat(){
System.out.println("我是学生,我要吃饭");
}
private String happy(String style){
return "我是学生,我要玩" + style;
}
public double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
public Integer getPkid() {
return pkid;
}
public void setPkid(Integer pkid) {
this.pkid = pkid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"weight=" + weight +
", pkid=" + pkid +
", name='" + name + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
3.反射的学习
反射可以取得类的哪些东西
/**
* 反射是指对于任何一个Class类,在”运行的时候“都可以直接得到这个类全部成员
* https://blog.csdn.net/weixin_60475929/article/details/122653101
*/
@Test
public void testString() throws ClassNotFoundException, NoSuchMethodException,
IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
// 1.通过类名.class获取类对象
Class<com.bilibili.juc.Student> studentClass = com.bilibili.juc.Student.class;
// 输出:studentClass:class com.bilibili.juc.Student
System.out.println("studentClass:" + studentClass);
// 2.通过实例对象.getClass()获取类对象
com.bilibili.juc.Student student = new com.bilibili.juc.Student();
Class<? extends com.bilibili.juc.Student> aClass = student.getClass();
// 输出:aClass:class com.bilibili.juc.Student
System.out.println("aClass:" + aClass);
// 3.通过Class.forName("类的全限定类名")获取类对象
Class<?> classForName = Class.forName("com.bilibili.juc.Student");
// 输出:classForName:class com.bilibili.juc.Student
System.out.println("classForName:" + classForName);
// 反射的第一步就是先得到编译后的Class对象,然后就可以得到Class的全部成分
// 获取到类对象后,就可以获取到类对应的构造方法、属性以及方法,用于创建对象或方法调用
/*
Class类中获取构造器的方法,获取构造器的作用:依旧是初始或一个对象返回
方法 说明
Constructor<?>[ ] getconstructors() 返回所有构造器对象的数组(只能拿public的)
Constructor<?>[] getDeclaredConstructors() 返回所有构造器对象的数组,存在就能拿到
constructor<T> getconstructor(class<?>... parameterTypes) 返回单个构造器对象(只能拿public的)
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回单个构造器对象,存在就能拿到
* */
// 提取类中的全部构造器对象(public)
Constructor<?>[] constructors = classForName.getConstructors();
// 遍历构造器
for (Constructor constructor : constructors) {
// 名称 + 参数
System.out.println("getConstructors:" + constructor.getName()+"=====>"
+ constructor.getParameterCount());
/**
* 输出:
* getConstructors:com.bilibili.juc.Student=====>0
* getConstructors:com.bilibili.juc.Student=====>1
*/
}
// 获取全部的构造器,无所谓权限是否可及
Constructor[] construct1 = classForName.getDeclaredConstructors();
for (Constructor constructor : construct1) {
System.out.println("getDeclaredConstructors:" + constructor.getName()
+ "====>" + constructor.getParameterCount());
/**
* 输出:
* getDeclaredConstructors:com.bilibili.juc.Student====>0
* getDeclaredConstructors:com.bilibili.juc.Student====>3
* getDeclaredConstructors:com.bilibili.juc.Student====>1
*/
}
// 获取某个构造器对象(按照参数定位无参构造器 只能拿public修饰某个构造器)
Constructor cons = classForName.getConstructor(Double.class);
// 名称 + 参数,输出:Double.class:com.bilibili.juc.Student=====>1
System.out.println("Double.class:" + cons.getName()
+ "=====>"+cons.getParameterCount());
// 获取某个构造器,无所谓权限是否可及
Constructor conss = classForName.getDeclaredConstructor(Double.class,
Integer.class, String.class);
// 名称 + 参数,输出:getDeclaredConstructor:com.bilibili.juc.Student=====>3
System.out.println("getDeclaredConstructor:" + conss.getName()
+ "=====>"+conss.getParameterCount());
System.out.println("-----------------------------------------");
/* Constructor类中用于创建对象的方法:
* 符号 说明
T newlnstance(Object... initargs) 根据指定的构造器创建对象
public void setAccessible(boolean flag) 设置为true,表示取消访问检查,进行暴力反射
* */
Constructor<?> declaredConstructor = classForName.getDeclaredConstructor(Double.class,
Integer.class, String.class);
// 暴力反射,因为这个有三个参数的构造是私有的
declaredConstructor.setAccessible(true);
com.bilibili.juc.Student newInstance =
(com.bilibili.juc.Student)declaredConstructor.newInstance(50.00, 25, "张三");
// 输出:newInstance:Student{weight=50.0, pkid=25, name='张三'}
System.out.println("newInstance:" + newInstance);
System.out.println("-------------------------------------");
/*
* class类中用于获取成员变量的方法
* 方法 说明
Field[] getFields() 返回所有成员变量对象的数组(只能拿public的)
Field[]getDeclaredFields() 返回所有成员变量对象的数组,存在就能拿到
Field getField(String name) 返回单个成员变量对象(只能拿public的)
Field getDeclaredField(String name) 返回单个成员变量对象,存在就能拿到
* */
Field[] declaredFields = classForName.getDeclaredFields();
for (Field declaredField : declaredFields) {
// getAnnotatedType:返回一个 AnnotatedType 对象,该对象表示使用类型来指定由该字段对象表示的字段的类型,
// 通过其 getType() 方法,我们可以获取到对应的字段类型
System.out.println("getDeclaredFields:" + declaredField.getName()+"====>"
+ declaredField.getAnnotatedType().getType());
/**
* 输出:
* getDeclaredFields:weight====>class java.lang.Double
* getDeclaredFields:pkid====>class java.lang.Integer
* getDeclaredFields:name====>class java.lang.String
*/
}
// 根据名称定位某个成员变量
Field name = classForName.getDeclaredField("name");
// 输出:getDeclaredField:name====>class java.lang.String
System.out.println("getDeclaredField:" + name.getName()+"====>"
+ name.getAnnotatedType().getType());
/*
* 获取成员变量的作用依然是在某个对象中取值、赋值,Field类中用于取值、赋值的方法
符号 说明
void set(Object obj, Object value): 赋值
Object get(Objectiobj) 获取值
* */
// 暴力打开权限,因为name属性是private的
name.setAccessible(true);
// 赋值
com.bilibili.juc.Student s = new com.bilibili.juc.Student();
name.set(s, "zhangshan");// s.setName("zhangsan")
// 输出:s:Student{weight=null, pkid=null, name='zhangshan'}
System.out.println("s:" + s);
String getName = (String)name.get(s);
// 输出:getName:zhangshan
System.out.println("getName:" + getName);
System.out.println("----------------------------------");
/*
* Class类中用于获取成员方法的方法
方法 说明
Method[ ] getMethods() 返回所有成员方法对象的数组(只能拿public的)
Method[ ] getDeclaredMethods() 返回所有成员方法对象的数组,存在就能拿到
Method getMethod(String name,class<?>... parameterTypes)
返回单个成员方法对象((只能拿public的), parameterTypes:参数类型
Method getDeclaredMethod(String name,Class<?>... parameterTypes)
返回单个成员方法对象,存在就能拿到, parameterTypes:参数类型
* */
// 提取所有方法,包括私有的
Method[] declaredMethods = classForName.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("方法名:" + declaredMethod.getName() + ",返回值类型:"
+ declaredMethod.getReturnType()
+ ",参数个数:" + declaredMethod.getParameterCount());
/**
* 输出:
* 方法名:study,返回值类型:void,参数个数:0
* 方法名:eat,返回值类型:void,参数个数:0
* 方法名:happy,返回值类型:class java.lang.String,参数个数:1
* 方法名:getWeight,返回值类型:double,参数个数:0
* 方法名:setWeight,返回值类型:void,参数个数:1
* 方法名:setPkid,返回值类型:void,参数个数:1
* 方法名:getPkid,返回值类型:class java.lang.Integer,参数个数:0
* 方法名:toString,返回值类型:class java.lang.String,参数个数:0
* 方法名:clone,返回值类型:class java.lang.Object,参数个数:0
* 方法名:getName,返回值类型:class java.lang.String,参数个数:0
* 方法名:setName,返回值类型:void,参数个数:1
*/
}
/*
* 获取成员方法的作用依然是在某个对象中进行执行此方法,Method类中用于触发执行的方法
符号 说明
Object invoke(0bject obj,object.. . args) 运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
* */
Method eat = classForName.getDeclaredMethod("eat");
Method happy = classForName.getDeclaredMethod("happy", String.class);
eat.setAccessible(true); // 打开暴力反射
happy.setAccessible(true); // 打开暴力反射
com.bilibili.juc.Student student1 = new com.bilibili.juc.Student();
Object invoke_eat = eat.invoke(student1);
// 如果方法是没有返回结果的,那么返回的为null
/**
* 输出:
* 我是学生,我要吃饭
* invoke_eat:null
*/
System.out.println("invoke_eat:" + invoke_eat);
Object invoke_happy = happy.invoke(student1, "游戏");
// 输出:invoke_happy:我是学生,我要玩游戏
System.out.println("invoke_happy:" + invoke_happy);
/*
* 反射的作用:绕过编程阶段为集合添加元素,
* 反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的
* 泛型只是在编译阶段可以约束集合只能操作某种数据类型,在编译成Class文件进入运行阶段的时候,
* 其真实类型都是ArrayList了,泛型相当于被擦除了。
* */
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(120);
// list1.add("加入数据"); // 报错
Class c = list1.getClass();
Method add = c.getDeclaredMethod("add", Object.class);
boolean rs = (boolean)add.invoke(list1, "加入数据");
// 输出:list1:[120, 加入数据]
System.out.println("list1:" + list1);
// 输出:rs:true
System.out.println("rs:" + rs);
/*
* 1.反射为何可以给约定了泛型的集合存入其他类型的元素?
编译成Class文件进入运行阶段的时候,泛型会自动擦除。
反射是作用在运行时的技术,此时已经不存在泛型了。
* */
/*
* 结论:反射的作用?
可以在运行时得到一个类的全部成分然后操作。
可以破坏封装性。(很突出)
也可以破坏泛型的约束性。(很突出)
更重要的用途是适合:做Java高级框架
* */
}
4.案例:利用反射存储任意对象
/*
分析
①定义一个方法,可以接收任意类的对象。
②每次收到一个对象后,需要解析这个对象的全部成员变量名称。
③这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
④使用反射获取对象的Class类对象,然后获取全部成员变量信息。
⑤遍历成员变量信息,然后提取本成员变量在对象中的具体值
⑥存入成员变量名称和值到文件中去即可。
* */
@Test
public void fansheTest(){
com.bilibili.juc.Student student = new com.bilibili.juc.Student(50.00);
save(student);
}
// 保存任意类型的对象
public static void save(Object obj){
try {
// 使用FileOutputStream时,如果文件不存在,会自动创建文件。但是,如果文件夹不存在,就会报错"系统找不到指定的路径"
PrintStream ps = new PrintStream(new FileOutputStream("D:\\test\\fanshe.txt",true));
// 1.提取对象的全部成员变量:只有反射可以解决
Class c = obj.getClass();
// c.getSimpleName()获取当前类名,c.getName获取全限名:包名+类名
ps.println("========>"+c.getSimpleName()+"======<");
// 2.提取全部成员变量
Field[] f = c.getDeclaredFields();
// 3.获取成员变量的信息
for (Field field : f) {
String name = field.getName();
field.setAccessible(true);
// 提取本成员变量在obj对象中的值(取值)
String value = field.get(obj) + "";
ps.println(name+"="+value);
}
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
喜欢请关注我
至此,我们的Java反射的学习
就讲解完成了。喜欢我的话可以关注我的微信公众号我爱学习呀嘻嘻
,不定期分享各类资源哦。