反射(reflection)
1. 一个需求引出反射
1.1 请看下面的问题
- 根据配置文件 re.properties 指定信息,创建Cat对象并调用方法hi
classfullpath=com.yujianedu.Cat
method=hi
老韩思考:使用现有技术,你能做的吗?
假如你没有了解学习反射相关的知识,我相信你是很难将怎么一个需求完成的
-
这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下来控制程序,也符合设计模式的ocp原则(开闭原则:不修改源码,扩容功能)
-
快速入门
- 我们先创建一个ReflectionQuestion.java的类,用来实现下上面的需求.
- 首先我们先创建一个Cat类,因为我们需要去调用其中的方法
package com.yujianedu;
public class Cat {
private String name = "招财猫";
public void hi(){
System.out.println("hi " + name);
}
public void cry(){
System.out.println(name + "哭了");
}
}
- 然后使用反射机制来调用Cat中的方法
步骤:
- 我们需要先将re.Properties中的 classfullpath,method的值先读取出来
- 使用之前IO中学到的Properties类,可以快速的将re.Properties中的值读取出来
- 然后再通过反射来实现Cat方法的使用
package com.yujianedu.reflection.question;
import com.yujianedu.Cat;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
//传统方式
//Cat cat = new Cat();
//cat.h1();
//1.使用Properties类,可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.getProperty("classfullpath").toString();
String methodName = properties.getProperty("method").toString();
System.out.println("classfullpath=" + classfullpath);
System.out.println("method=" + methodName);
//2.使用反射机制来解决
// (1)加载类,返回Class类型的对象 cls
Class cls = Class.forName(classfullpath);
//(2)通过 cls 得到你加载的类 com.yujianedu.Cat 的对象实例
Object o = cls.newInstance();
System.out.println("o的运行类型是:"+o.getClass()); //得到运行类型
//(3)通过 cls 得到你加载类 com.yujianedu.Cat 的 methodName(hi) 的方法对象
Method method = cls.getMethod(methodName);
System.out.println("====================");
//(4)通过 method 来调用方法 : 即通过方法对象来实现调用方法
method.invoke(o); //传统方法 对象.方法() 反射机制 方法.invoke(对象)
}
}
执行结果:
注:假如在没有学习反射之前,我们假如不需要了这个hi方法,我们需要调用Cat类中其他的方法,我们一般的做法是,先创建一个Cat对象(假如已经创建过来,就不用再创建),然后让 该对象.要实现的方法. (这样子做,我们就对源代码进行了修改),假如使用反射,我们就可以避免这个操作(不用去修改原来的代码),我们只需要去修改配置文件就可以
比如我们的Cat类中有一个cry()方法,现在,我们不想调用hi()方法了,我们想要去调用cry()方法,我们只需要将re.properties 中method的值改为cry,就可以实现
classfullpath=com.yujianedu.Cat
method=cry
ReflectionQuestion 类我们没有修改,现在我们再调用,看看得到的结果是什么?
那么下面,我们就来好好的了解下,到底什么是反射机制?
2. 反射机制
2.1 Java Reflection
-
反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
-
加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射
2.2 Java 反射机制原理示意图!!!
2.3 Java 反射机制可以完成
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时得到任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的成员变量和方法
5.生成动态代理
2.4 反射相关的主要类
1.java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
2.java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
3.java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
4.java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
上面的这些类都在java.lang.reflect中
下面我们再举个例子,来更好的了解上面4个类的相关用法
package com.yujianedu.reflection;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class Reflection01 {
public static void main(String[] args) throws Exception {
//1.使用Properties类,可以读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.getProperty("classfullpath");
String methodName = properties.getProperty("method");
//2.使用反射机制来解决
// (1)加载类,返回Class类型的对象 cls
Class cls = Class.forName(classfullpath);
//(2)通过 cls 得到你加载的类 com.yujianedu.Cat 的对象实例
Object o = cls.newInstance();
System.out.println("o的运行类型是:"+o.getClass()); //得到运行类型
//(3)通过 cls 得到你加载类 com.yujianedu.Cat 的 methodName(hi) 的方法对象
Method method = cls.getMethod(methodName);
System.out.println("====================");
//(4)通过 method 来调用方法 : 即通过方法对象来实现调用方法
method.invoke(o); //传统方法 对象.方法() 反射机制 方法.invoke(对象)
//java.lang.reflect.Field: 代表类的成员变量, Field 对象表示某个类的成员变量
//得到 name 字段
//getField 不能得到私有的属性
Field nameField = cls.getField("age");
System.out.println(nameField.get(o)); // 传统写法 对象.成员变量 , 反射 : 成员变量对象.get(对象)
//java.lang.reflect.Constructor: 代表类的构造方法, Constructor 对象表示构造器
Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型, 返回无参构造器
System.out.println(constructor);
Constructor constructor1 = cls.getConstructor(String.class); //这里老师传入的 String.class 就是 String 类的 Class 对象
System.out.println(constructor1);
}
}
执行结果:
2.5 反射优点和缺点
1.优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
2.缺点:使用反射基本是解释执行,对执行速度有影响.
3.应用实例:Reflection02.java com.yujianedu.reflecton
package com.yujianedu.reflection;
import com.yujianedu.Cat;
import java.lang.reflect.Method;
/**
* 测试反射调用的性能,和优化方案
*/
public class Reflection02 {
public static void main(String[] args) throws Exception {
m1(); //传统
m2(); //反射
m3();//优化反射
}
//传统方法来调用 hi
public static void m1(){
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("m1() 运行时间为 : "+(end-start));
}
//反射机制调用方法 hi
public static void m2() throws Exception {
Class cls = Class.forName("com.yujianedu.Cat");
Object o = cls.newInstance();
Method method = cls.getMethod("hi");
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
method.invoke(o); //反射调用方法
}
long end = System.currentTimeMillis();
System.out.println("m2() 运行时间为 : "+(end-start));
}
}
2.6 反射调用优化-关闭访问检查
1.Method和Field、Constructor对象都有setAccessible()方法
2.setAccessible作用是启动和禁用访问安全检查的开关
3.参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查
代码如下:
//反射调用优化 + 关闭访问检查
public static void m3() throws Exception {
Class cls = Class.forName("com.yujianedu.Cat");
Object o = cls.newInstance();
Method method = cls.getMethod("hi");
method.setAccessible(true);//在反射调用方法时,取消访问检查
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
method.invoke(o); //反射调用方法
}
long end = System.currentTimeMillis();
System.out.println("m3() 运行时间为 : "+(end-start));
}
执行结果:
发现 关闭访问检查 之后,运行时间是真的减少了.
3. Class 类
3.1 基本介绍
1.Class也是类,因此也继承Object类[类图]
2.Class类对象不是new出来的,而是系统创建的[演示]
3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次[演示]
4.每个类的实例都会记得自己是由哪个Class 实例所生成5.通过Class对象可以完整地得到一个类的完整结构,通过一系列APl
6.Class对象是存放在堆的
7.类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,变量名,方法名,访问权限等等)https://www.zhihu.com/question/38496907【示意图】
代码演示:
package com.yujianedu.reflection.class_;
public class Class01_ {
public static void main(String[] args) throws ClassNotFoundException {
//看看 Class 类图
//1. Class 也是类,因此也继承 Object 类
//Class
//2. Class 类对象不是 new 出来的,而是系统创建的
//(1) 传统 new 对象
/* ClassLoader 类
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
*/
//Cat cat = new Cat();
//(2) 反射方式, 刚才老师没有 debug 到 ClassLoader 类的 loadClass, 原因是,我没有注 销 Cat cat = newCat();
/*
ClassLoader 类, 仍然是通过 ClassLoader 类加载 Cat 类的 Class 对象
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
*/
Class cls1 = Class.forName("com.yujianedu.Cat");
//3. 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次
Class cls2 = Class.forName("com.yujianedu.Cat");
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
Class cls3 = Class.forName("com.yujianedu.Dog");
System.out.println(cls3.hashCode());
}
}
执行结果:
3.2 Class 类的常用方法
3.3 应用实例: Class02.java
package com.yujianedu.reflection.class_;
import com.yujianedu.Car;
import java.lang.reflect.Field;
/**
* 演示 Class 类的常用方法
*/
public class Class02_ {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
String classAllPath = "com.yujianedu.Car";
//1 . 获取到 Car 类 对应的 Class 对象
//<?> 表示不确定的 Java 类型
Class<?> cls = Class.forName(classAllPath);
//2. 输出 cls
System.out.println(cls); //显示 cls 对象, 是哪个类的 Class 对象 com.hspedu.Car
System.out.println(cls.getClass()); //输出 cls 运行类型 java.lang.Class
//3. 得到包名
System.out.println(cls.getPackage().getName()); //包名
//4. 得到全类名
System.out.println(cls.getName());
//5. 通过 cls 创建对象实例
Car car = (Car) cls.newInstance();
System.out.println(car);
//6. 通过反射获取属性 brand
Field brand = cls.getField("brand");
System.out.println(brand); //public java.lang.String com.yujianedu.Car.brand
System.out.println(brand.getName()); //获得字段的名称 brand
System.out.println(brand.get(car)); //获得字段的值 宝马
//7. 通过反射给属性赋值
brand.set(car,"奥迪");
System.out.println(brand.get(car));
System.out.println("==========所有字段属性==========");
//8 我希望大家可以得到所有的属性(字段)
Field[] fields = cls.getFields();
for(Field f : fields){
System.out.println(f.getName()+":"+f.get(car));
}
}
}
执行结果:
4. 获取 Class 类对象
总共有 6 种方式可以来获取 Class 类对象,下面我们就一一进行举例
-
前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,实例:Class cls1=Class.forName("java.lang.Cat");
应用场景:多用于配置文件,读取类全路径,加载类.
//1. Class.forName
String classAllPath = "com.yujianedu.Car"; //一般都是通过读取配置文件获得
Class cls1 = Class.forName(classAllPath);
System.out.println(cls1);
-
前提:若已知具体的类,通过类的class 获取,该方式最为安全可靠,程序性能最高
实例:Class cls2=Cat.class;
应用场景:多用于参数传递,比如通过反射得到对应构造器对象。
//2. 类名.class ,应用场景:用于参数传递
Class<Car> cls2 = Car.class;
System.out.println(cls2);
-
前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:
Class clazz=对象.getClass);//运行类型应用场景:通过创建好的对象,获取Class对象.
//3. 对象.getClass() ,应用场景:有对象实例
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3);
-
其他方式
ClassLoader cl=对象.getClass().getClassLoader();
Class clazz4=cl.loadClass("类的全类名”);
//4. 通过类加载器 [4种] 来获取类的Class对象
//(1) 先得到类加载器 car
ClassLoader classLoader = car.getClass().getClassLoader();
//(2) 通过类加载器得到Class对象
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
执行结果:
-
基本数据(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类对象
-
基本数据类型对应的包装类,可以通过。TYPE得到Class类对象
代码如下:
//5.基本数据(int,char,boolean,fLoat,double,byte,Long,short)按如下方式得到CLass类对象
Class<Integer> integerClass = int.class;
Class<Boolean> booleanClass = boolean.class;
Class<Character> characterClass = char.class;
System.out.println(integerClass); //int
//6.基本数据类型对应的包装类,可以通过。TYPE得到CLass类对象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE;
System.out.println(type1);
执行结果:
5. 哪些类型有 Class 对象
5.1 如下类型有 Class 对象
1.外部类,成员内部类,静态内部类,局部内部类,匿名内部类
2.interface:接口
3.数组
4.enum:枚举
5.annotation:注解
6.基本数据类型
7.void
5.2 应用实例 AllTypeClass.java
代码如下:
package com.yujianedu.reflection.class_;
import java.io.Serializable;
/**
* 演示哪些类型有 Class 对象
*/
public class AllTypeClass {
public static void main(String[] args) {
Class<String> cls1 = String.class;//外部类
Class<Serializable> cls2 = Serializable.class;//接口
Class<Integer[]> cls3 = Integer[].class;//数组
Class<float[][]> cls4 = float[][].class;//二维数组
Class<Deprecated> cls5 = Deprecated.class;//注解
//枚举
Class<Thread.State> cls6 = Thread.State.class;
Class<Long> cls7 = long.class;//基本数据类型
Class<Void> cls8 = void.class;//void 数据类型
Class<Class> cls9 = Class.class;
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);
System.out.println(cls5);
System.out.println(cls6);
System.out.println(cls7);
System.out.println(cls8);
System.out.println(cls9);
}
}
执行结果:
6. 类加载
6.1 基本说明
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
1.静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
2.动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性
3.举例说明 (创建一个 ClassLoad_ 来说明下什么是静态加载,什么是动态加载)
package com.yujianedu.reflection.class_;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;
public class ClassLoad_ {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Scanner in = new Scanner(System.in);
System.out.println("请输入key");
String key = in.next();
switch (key){
case "1":
Animal animal = new Animal();
animal.cry();
break;
case "2":
Class cls = Class.forName("Person");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
hi.invoke(o);
System.out.println("ok");
break;
default:
System.out.println("do nothing...");
}
}
}
从上面这个程序可以知道,假如你的程序中并没有Animal
这个类,那么你的代码就无法正常的执行,你必须有Animal
这个类,才可以正常的执行代码
就像这个图表示的,它会爆红,上面展示的这个就是静态加载,在编译的时候必须加上相对于的类,依赖性太强
所以我们需要先创建一个Animal
类
class Animal{
public void cry(){
System.out.println("wuwuwuwuu~~~");
}
}
创建完之后就可以确保可以编译通过
但反射机制就是动态加载,只有你使用的时候才会时候才会去检查你是否有相对于的类,假如没有,才会报错.
6.2 类加载时机
1.当创建对象时(new)//静态加载
2.当子类被加载时,父类也加载//静态加载
3.调用类中的静态成员时//静态加载
4.通过反射//动态加载
6.3 类加载过程图
6.4 类加载各阶段完成任务
6.5 加载阶段
JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是class 文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class 对象
6.6 连接阶段-验证
1.目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
2.包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证[举例说明]
3.可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
6.7 连接阶段-准备
1.JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0、OL、null、false等)。这些变量所使用的内存都将在方法区中进行分配
2.举例说明:ClassLoad02.java
public class ClassLoad02 {
public static void main(String[] args) {
}
}
class A {
//属性-成员变量-字段
//老韩分析类加载的链接阶段-准备 属性是如何处理
//1. n1 是实例属性, 不是静态变量,因此在准备阶段,是不会分配内存
//2. n2 是静态变量,分配内存 n2 是默认初始化 0 ,而不是 20
//3. n3 是 static final 是常量, 他和静态变量不一样, 因为一旦赋值就不变 n3 = 30
public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;
}
6.8 连接阶段-解析
1.虚拟机将常量池内的符号引用替换为直接引用的过程。
6.9 Initialization(初始化)
1.到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行
2.
3.虚拟机会保证一个类的
public class ClassLoad03 {
public static void main(String[] args) throws ClassNotFoundException {
//老韩分析
//1. 加载 B 类,并生成 B 的 class 对象
//2. 链接 num = 0
//3. 初始化阶段
// 依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并
/*
clinit() {
System.out.println("B 静态代码块被执行");
//num = 300;
num = 100;
}
合并: num = 100
*/
//new B();//类加载
//System.out.println(B.num);//100, 如果直接使用类的静态属性,也会导致类的加载
//看看加载类的时候,是有同步机制控制
/*
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
//正因为有这个机制,才能保证某个类在内存中, 只有一份 Class 对象
synchronized (getClassLoadingLock(name)) {
//.... }
}
*/
B b = new B();
}
}
class B {
static {
System.out.println("B 静态代码块被执行");
num = 300;
}
static int num = 100;
public B() {//构造器
System.out.println("B() 构造器被执行");
}
}
7. 通过反射获取类的结构信息
7.1 第一组: java.lang.Class 类
1.getName:获取全类名
2.getSimpleName:获取简单类名
3.getFields:获取所有public修饰的属性,包含本类以及父类的
4.getDeclaredFields:获取本类中所有属性
5.getMethods:获取所有public修饰的方法,包含本类以及父类的
6.getDeclaredMethods:获取本类中所有方法
7.getConstructors:获取本类所有public修饰的构造器
8.getDeclaredConstructors:获取本类中所有构造器
9.getPackage:以Package形式返回包信息
10.getSuperClass:以Class形式返回父类信息
11.getlnterfaces:以Class[]形式返回接口信息
12.getAnnotations:以Annotation[]形式返回注解信息
代码如下:
package com.yujianedu.reflection;
public class ReflectionUtils {
public static void main(String[] args) {
}
@Test
public void api_01() throws ClassNotFoundException {
Class personCls = Class.forName("com.yujianedu.reflection.Person");
//1.getName:获取全类名
System.out.println(personCls.getName()); // com.yujianedu.reflection.Person
//2.getSimpleName:获取简单类名
System.out.println(personCls.getSimpleName()); // Person
//3.getFields:获取所有public修饰的属性,包含本类以及父类的
Field[] fields = personCls.getFields();
for (Field field : fields) {
System.out.println("本类以及父类的属性:"+field.getName());
}
//4.getDeclaredFields:获取本类中所有属性
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本类中的所有属性:"+declaredField.getName());
}
//5.getMethods:获取所有public修饰的方法,包含本类以及父类的
Method[] methods = personCls.getMethods();
System.out.println("本类以及父类的所有方法");
for (Method method : methods) {
System.out.println(method.getName());
}
//6.getDeclaredMethods:获取本类中所有方法
System.out.println("获取本类中的所有方法");
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName());
}
//7.getConstructors:获取本类所有public修饰的构造器
System.out.println("获取本类所有public修饰的构造器");
Constructor[] constructors = personCls.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor.getName());
}
//8.getDeclaredConstructors:获取本类中所有构造器
System.out.println("获取本类中所有构造器");
Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}
//9.getPackage:以Package形式返回包信息
System.out.println("获得当前所在包的路径");
System.out.println(personCls.getPackage());
//10.getSuperClass:以Class形式返回父类信息
System.out.println("获得当前类父类的信息");
System.out.println(personCls.getSuperclass());
//11.getInterfaces:以Class[]形式返回接口信息
System.out.println("接口信息");
Class[] interfaces = personCls.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println(anInterface.getName());
}
//12.getAnnotations:以Annotation[]形式返回注解信息
System.out.println("注解信息");
AnnotatedType[] annotatedInterfaces = personCls.getAnnotatedInterfaces();
for (AnnotatedType annotatedInterface : annotatedInterfaces) {
System.out.println(annotatedInterface);
}
}
}
class A {
public String hobby;
public void hi() {
}
public A() {
}
public A(String name) {
}
}
interface IA {
}
interface IB {
}
@Deprecated
class Person extends A implements IA, IB {
//属性
public String name;
protected static int age; // 4 + 8 = 12
String job;
private double sal;
//构造器
public Person() {
}
public Person(String name) {
}
//私有的
private Person(String name, int age) {
}
//方法
public void m1(String name, int age, double sal) {
}
protected String m2() {
return null;
}
void m3() {
}
private void m4() {
}
}
执行结果:
com.yujianedu.reflection.Person
Person
本类以及父类的属性:name
本类以及父类的属性:hobby
本类中的所有属性:name
本类中的所有属性:age
本类中的所有属性:job
本类中的所有属性:sal
本类以及父类的所有方法
m1
hi
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
获取本类中的所有方法
m1
m2
m4
m3
获取本类所有public修饰的构造器
com.yujianedu.reflection.Person
com.yujianedu.reflection.Person
获取本类中所有构造器
com.yujianedu.reflection.Person
com.yujianedu.reflection.Person
com.yujianedu.reflection.Person
获得当前所在包的路径
package com.yujianedu.reflection
获得当前类父类的信息
class com.yujianedu.reflection.A
接口信息
com.yujianedu.reflection.IA
com.yujianedu.reflection.IB
注解信息
sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@4dcbadb4
sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@4e515669
7.2 第二组: java.lang.reflect.Field 类
1.getModifiers:以int形式返回修饰符[说明:默认修饰符是0,public 是1,private是2,protected是4,static是8,final是16],public(1)+static(8)=9
2.getType:以Class形式返回类型
3.getName:返回属性名
@Test
public void api_02() throws ClassNotFoundException {
//得到Class对象
Class personCls = Class.forName("com.yujianedu.reflection.Person");
//getDecLaredFields:获取本类中所有属性
//说明:默认修饰符是0,public 是1,private是2,protected是4,static是8,final是16]
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本类中的所有属性:"+declaredField.getName()
+ "该属性的修饰符值:" + declaredField.getModifiers()
+ "该属性的类型:" + declaredField.getType());
}
}
执行结果:
本类中的所有属性:name该属性的修饰符值:1该属性的类型:class java.lang.String
本类中的所有属性:age该属性的修饰符值:12该属性的类型:int
本类中的所有属性:job该属性的修饰符值:0该属性的类型:class java.lang.String
本类中的所有属性:sal该属性的修饰符值:2该属性的类型:double
7.3 第三组: java.lang.reflect.Method 类
1.getModifiers:以int形式返回修饰符[说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16]
2.getReturnType:以Class形式获取返回类型
3.getName:返回方法名
4.getParameterTypes:以Class]返回参数类型数组
代码如下:
@Test
public void api_03() throws ClassNotFoundException {
//得到Class对象
Class personCls = Class.forName("com.yujianedu.reflection.Person");
//说明:默认修饰符是0,public 是1,private是2,protected是4,static是8,final是16]
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod.getName()
+ "该方法的访问修饰符值:"+declaredMethod.getModifiers()
+ "该方法的返回类型" + declaredMethod.getReturnType());
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该方法的形参类型:" + parameterType);
}
}
}
执行结果:
m1该方法的访问修饰符值:1该方法的返回类型void
该方法的形参类型:class java.lang.String
该方法的形参类型:int
该方法的形参类型:double
m2该方法的访问修饰符值:4该方法的返回类型class java.lang.String
m4该方法的访问修饰符值:2该方法的返回类型void
m3该方法的访问修饰符值:0该方法的返回类型void
7.4 第四组: java.lang.reflect.Constructor 类
1.getModifiers:以int形式返回修饰符
2.getName:返回构造器名(全类名)
3.getParameterTypes:以Class]返回参数类型数组
//getDeclaredConstructors:获取本类中所有构造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("====================");
System.out.println("本类中所有构造器=" + declaredConstructor.getName());//这里老师只是输出名
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该构造器的形参类型=" + parameterType);
}
8. 通过反射创建对象
1.方式一:调用类中的public修饰的无参构造器
2.方式二:调用类中的指定构造器
3.Class类相关方法
- newlnstance:调用类中的无参构造器,获取对应类的对象
- getConstructor(Clas...clazz):根据参数列表,获取对应的public构造器对象
- getDecalaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器对象
4.Constructor类相关方法
- setAccessible:暴破
- newlnstance(Object...obj):调用构造器
8.1 案例演示 com.yujian.reflection ReflectCreateInstance.java
测试 1:通过反射创建某类的对象,要求该类中必须有 public 的无参构造
测试 2:通过调用某个特定构造器的方式,实现创建某类的对象
代码如下:
package com.yujianedu.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1.先获得User类的对象
Class<?> userClass = Class.forName("com.yujianedu.reflection.User");
//2.通过public的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println("通过public的无参构造器创建实例");
System.out.println(o);
//3.通过public有参构造器创建实例
System.out.println("通过public有参构造器创建实例");
/*
public User(String name) {//public 的有参构造器
this.name = name;
}
*/
//3.1 先得到对应的构造器
Constructor<?> constructor = userClass.getConstructor(String.class);
//3.2 创建实例,并传入实例
//这里就相当于 new User("yujian")
Object yujian = constructor.newInstance("yujian"); //yujian相当于传入的name
System.out.println("Goin学习" + yujian);
//4. 通过非public 有参构造器创建实例
System.out.println("通过非public 有参构造器创建实例");
//4.1 得到private的构造对象
Constructor<?> cconstructor1 = userClass.getDeclaredConstructor(int.class, String.class);
//4.2 创建实例
// 暴破【暴力破解】,使用反射可以访问private构造器/方法/属性,反射面前,都是纸老虎
cconstructor1.setAccessible(true);
Object user1 = cconstructor1.newInstance(20, "王也道长");
System.out.println(user1);
}
}
class User { //User 类
private int age = 10;
private String name = "yujian学习反射";
public User() {//无参 public
}
public User(String name) {//public 的有参构造器
this.name = name;
}
private User(int age, String name) {//private 有参构造器
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
}
执行结果:
通过public的无参构造器创建实例
User [age=10, name=yujian学习反射]
通过public有参构造器创建实例
Goin学习User [age=10, name=yujian]
通过非public 有参构造器创建实例
User [age=20, name=王也道长]
9. 通过反射访问类中的成员
9.1 访问属性 ReflecAccessProperty.java
1.根据属性名获取Field对象
Field f=class对象.getDeclaredField(属性名);
2.暴破:f.setAccessible(true);//f 是Field
3.访问
f.set(o,值);//o表示对象
syso(f.get(o));//o 表示对象
4.注意:如果是静态属性,则set和get中的参数o,可以写成null
代码如下:
package com.yujianedu.reflection;
import java.lang.reflect.Field;
public class ReflectAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.得到Student类对应的Class对象
Class<?> stuClass = Class.forName("com.yujianedu.reflection.Student");
//1. 创建对象
Object o = stuClass.newInstance(); //o 的运行类型是Srudent
System.out.println(o.getClass()); // Student
//3.使用反射得到age的属性对象
Field age = stuClass.getField("age");
age.set(o,22);
System.out.println(o);
System.out.println(age.get(o)); //返回age属性的值
//4. 使用反射操作私有属性name
Field name = stuClass.getDeclaredField("name");
//对 name进行爆破,可以操作private 属性
name.setAccessible(true);
//name.set(o,"yujain");
name.set(null,"yujian"); //因为name是static属性,因此 o可以写成 null
System.out.println(o);
System.out.println(name.get(o)); //获得属性值
System.out.println(name.get(null)); //获得属性值 ,要求name 是static 属性
}
}
class Student {//类
public int age;
private static String name;
public Student() {//构造器
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
}
执行结果:
class com.yujianedu.reflection.Student
Student [age=22, name=null]
22
Student [age=22, name=yujian]
yujian
yujian
9.2 访问方法 ReflecAccessMethod.java
1.根据方法名和参数列表获取Method方法对象:Method m = clazz.getDeclaredMethod(方法名,XX.class);//得到本类的所有方法
2.获取对象:Object o=clazz.newlnstance();
3.暴破:m.setAccessible(true);
4.访问:Object returnValue=m.invoke(o,实参列表);/∥o就是对象
5.注意:如果是静态方法,则invoke的参数o,可以写成null!
代码如下:
package com.yujianedu.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1. 得到Boss对应的Class对象
Class<?> bosscls = Class.forName("com.yujianedu.reflection.Boss");
//2. 创建对象
Object o = bosscls.newInstance();
//3. 调用public 的 hi方法
Method hi1 = bosscls.getDeclaredMethod("hi", String.class);
//Method hi2 = bosscls.getMethod("hi", String.class);
//3.2 调用
hi1.invoke(o,"yujian,努力学习");
//4. 调用 private static 方法
//4.1 得到 say 方法对象
Method say = bosscls.getDeclaredMethod("say", int.class, String.class, char.class);
//4.2 因为 say 方法是 private, 所以需要暴破,原理和前面讲的构造器和属性一样
say.setAccessible(true);
System.out.println(say.invoke(o, 100, "张三", '男'));
//4.3 因为 say 方法是 static 的,还可以这样调用 ,可以传入 null
System.out.println(say.invoke(null, 200, "李四", '女'));
//5. 在反射中,如果方法有返回值,统一返回 Object , 但是他运行类型和方法定义的返回类型一致
Object reVal = say.invoke(null, 300, "王五", '男');
System.out.println("reVal 的运行类型=" + reVal.getClass());//String
//在演示一个返回的案例
Method m1 = bosscls.getDeclaredMethod("m1");
Object reVal2 = m1.invoke(o);
System.out.println("reVal2 的运行类型=" + reVal2.getClass());//Monster
}
}
class Monster {
}
class Boss {//类
public int age;
private static String name;
public Boss() {//构造器
}
public Monster m1() {
return new Monster();
}
private static String say(int n, String s, char c) {//静态方法
return n + " " + s + " " + c;
}
public void hi(String s) {//普通 public 方法
System.out.println("hi " + s);
}
}
执行结果:
hi yujian,努力学习
100 张三 男
200 李四 女
reVal 的运行类型=class java.lang.String
reVal2 的运行类型=class com.yujianedu.reflection.Monster
10. 10本章作业
-
练习1:通过反射修改私有成员变量 com.hspedu.homework Homework01.java
1.定义PrivateTest类,有私有name属性,并且属性值为hellokitty
2.提供getName的公有方法
3.创建PrivateTest的类,利用Class类得到私有的name属性,修改私有的name属性值,并调用getName()的方法打印name属性值
代码如下:
package com.yujianedu.reflection.homework;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class HomeWork01 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//1.通过Class类来获取PrivateTest对象
Class<?> testcls = Class.forName("com.yujianedu.reflection.homework.PrivateTest");
Object o = testcls.newInstance();
//通过Class类 得到 PrivateTest 的私有属性name
Field nameField = testcls.getDeclaredField("name");
//修改name的值.因为name属性是私有字段,所以我们想要修改,只能进行暴破
nameField.setAccessible(true);
System.out.println("nameField对应字段是:" + nameField.getName());
System.out.println("nameField的值是:" + nameField.get(o));
//将字段name的值进行修改
nameField.set(o,"yujian");
//调用 getName()
Method getName = testcls.getMethod("getName");
System.out.println("修改完之后,name属性的值为:"+getName.invoke(o));
}
}
class PrivateTest{
private String name = "helloKitty";
public String getName(){
return name;
}
}
执行结果:
nameField对应字段是:name
nameField的值是:helloKitty
修改完之后,name属性的值为:yujian
练习2利用反射和File完成以下功能 Homework02.java
1.利用Class类的forName方法得到File类的class对象
2.在控制台打印File类的所有构造器
3.通过newlnstance的方法创建File对象,并创建文件 d:\reflection.txt
提示:创建文件的正常写法如下:
File file=new File("d:\aa.txt");//内存
file.createNewFile();/方法,才能真正的创建文件
代码如下:
package com.yujianedu.reflection.homework;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class HomeWork02 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> fileCls = Class.forName("java.io.File");
System.out.println("File类下的所有构造器");
for (Constructor<?> constructor : fileCls.getConstructors()) {
System.out.println(constructor);
}
Constructor<?> constructor = fileCls.getConstructor(String.class);
Object o = constructor.newInstance("d:\\reflection.txt");
Method createNewFile = fileCls.getMethod("createNewFile") ;
createNewFile.invoke(o);
}
}
执行结果:
File类下的所有构造器
public java.io.File(java.lang.String,java.lang.String)
public java.io.File(java.lang.String)
public java.io.File(java.io.File,java.lang.String)
public java.io.File(java.net.URI)
并且 d:\reflection.txt 该文件也创建成功~~