反射
反射JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(包含private修饰的);对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。在反射面前, 一切都是赤裸裸的.反射相关的主要APIjava.lang.Class : 代表一个类java.lang.reflect.Method : 代表类中方法ja
·
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(包含private修饰的);对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
在反射面前, 一切都是赤裸裸的.
反射相关的主要API
java.lang.Class : 代表一个类
java.lang.reflect.Method : 代表类中方法
java.lang.reflect.Field : 代表类中的成员变量
java.lang.reflect.Constructor : 代表类中的构造方法
Student类 -> 字节码对象
name属性 -> Field对象
eat()方法 -> Method对象
构造方法 -> Constructor对象
类加载的过程
- java源代码经过javac.exe命令之后, 会生成一个或多个字节码文件(Xxx.class)
- 使用java.exe命令对字节码文件进行运行, 这个过程中会将Xxx.class加载进内存.(类的加载)
- 此时在内存中就出现了一个Xxx.class, 这个Xxx.class其实就是Class类的对象, 也是反射需要操作的内容.
类加载的时机(什么时候用, 什么时候加载)
- 创建对象
- 使用类方法(静态方法)
- 使用类变量(静态变量)
- 使用反射创建对象
- 使用它的子类
- 使用java.exe命令执行
获取字节码对象的3种方式
- 源文件阶段
- 字节码文件阶段
- 创建对象
扩展: ClassLoader
引导类加载器(Bootstap Classloader) : 用C++编写, 是JVM自带的类加载器, 负责Java平台的核心类库.
扩展类加载器(Extension Classloader) (jdk1.7以及以前)
扩展类加载器(PlatformClassLoader)(jdk1.8开始) : 负责jre/lib/ext目录下的jar包内容的加载
系统类加载器(System Classloader) : 负责java.exe运行时
/**
*
* 获取字节码对象的三种方式
*/
public class demo02 {
public static void main(String[] args) throws Exception{
// Class.forName()
Class aClass1 = Class.forName("com.itheima._02reflect.demo02.Person");
// 类名.class
Class aClass2 = Person.class;
// 对象名.getClass()
Person p = new Person();
Class aClass3 = p.getClass();
Person p2 = new Person("高圆圆", 30);
Class aClass4 = p2.getClass();
System.out.println(aClass1 == aClass2); // true
System.out.println(aClass2 == aClass3); // true
System.out.println(aClass4 == aClass3); // true
System.out.println(aClass1.hashCode()); // 434091818
System.out.println(aClass2.hashCode()); // 434091818
System.out.println(aClass3.hashCode()); // 434091818
System.out.println(aClass4.hashCode()); // 434091818
}
}
动态获取
构造方法
(1) 获取空参构造并创建对象
- newInstance()
/**
* 使用反射, 获取空参构造创建对象
*
* public T newInstance()
*
*/
public class Demo03 {
public static void main(String[] args) throws Exception{
// 获取字节码对象
Class aClass1 = Class.forName("com.itheima._02reflect.demo02.Person");
// 创建对象
Person p = (Person) aClass1.newInstance();
p.setName("贾静雯");
p.setAge(20);
System.out.println(p);
}
}
(3) 获取有参构造并创建对象
// 通过字节码对象, 获取构造方法的对象(Constructor)
public Constructor<?>[] getConstructors() : 获取所有构造方法的对象(Constructor)
public Constructor<T> getConstructor(Class<?>... parameterTypes)
Class : 参数中要传入Class类型的参数
Class ... : 可变参数
parameterTypes : 参数类型
参数中需要传入的是Class类型的多个参数, 用来描述构造方法的参数
有很多构造方法, 我们来区分到底使用哪个构造, 需要通过参数列表来区分
构造方法() => getConstructor()
构造方法(String name) => getConstructor(String.class)
构造方法(String name, int age) => getConstructor(String.class, int.class)
- getConstructors()
// 获取字节码对象
Class aClass = Class.forName("com.itheima._02reflect.demo02.Person");
// 获取所有构造方法
Constructor[] constructors = aClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
- 打印效果
获取指定的构造方法
// 获取字节码对象
Class aClass = Class.forName("com.itheima._02reflect.demo02.Person");
// 获取指定构造方法
Constructor c = aClass.getConstructor(String.class, int.class);
System.out.println(c);
使用构造方法, 创建对象
// 原始创建对象: new Person("张三", 23)
// Person() => 构造方法
// "张三", 23 => 参数
// 刚才已经能够获取到Constructor构造方法, 还剩下传参
public T newInstance(Object... initargs)
// Constructor : c
c.newInstance() => new 类名()
c.newInstance("张三") => new 类名("张三")
c.newInstance("张三", 23) => new 类名("张三", 23)
- 代码演示
/**
* 获取指定构造方法
*/
public class Demo03_3 {
public static void main(String[] args) throws Exception {
// 获取字节码对象
Class aClass = Class.forName("com.itheima._02reflect.demo02.Person");
// 获取指定构造方法
Constructor c = aClass.getConstructor(String.class, int.class);
// 调用newInstance方法, 传入参数并创建对象
Person p = (Person) c.newInstance("张三", 23);
System.out.println(p);
// 获取空参构造
Constructor c2 = aClass.getConstructor();
// 创建对象
Person p2 = (Person) c2.newInstance();
System.out.println(p2);
}
}
5. 成员变量
(1) 获取成员变量并修改
获取成员变量
// 成员变量: Field类的对象
// 获取所有成员变量
public Field[] getFields()
public Field[] getDeclaredFields() : 暴力获取到所有成员变量(包括私有的)
// 获取指定的成员变量
// 根据变量的名字, 获取唯一的成员变量
public Field getField(String name)
getField("name") -> name成员变量
修改成员变量的值
public void set(Object obj, // 对象
Object value) // 值
获取非私有成员变量并修改
// 获取字节码对象
Class aClass = Class.forName("com.itheima._02reflect.demo02.Person");
// 获取构造方法
Constructor c = aClass.getConstructor();
// 创建对象
Person p = (Person) c.newInstance();
// 获取指定成员变量 (Field)
Field gender = aClass.getField("gender");
// 传统方式的修改: p.gender = "男";
gender.set(p, "男"); // 修改p中的gender属性, 改成"男"
System.out.println(p);
(2) 暴力反射获取私有成员变量并修改
获取私有成员变量并修改(暴力反射)
// public Field getDeclaredField(String name) : 获取私有成员变量
// public void setAccessible(boolean flag) : 去除私有权限 -> true
// 获取字节码对象
Class aClass = Class.forName("com.itheima._02reflect.demo02.Person");
// 获取无参构造
Constructor c = aClass.getConstructor();
// 使用无参构造, 创建对象
Person p = (Person) c.newInstance();
// 获取私有成员变量
Field name = aClass.getDeclaredField("name");
// 去除私有权限
name.setAccessible(true);
// 修改成员变量值
/*
set(Object obj, // 修改的对象
Object value) // 修改到的值
*/
name.set(p, "张三丰");
System.out.println(p);
6. 成员方法(Method)
反射调用方法
无参时
私有方法,无参
(1) 获取所有成员方法
public Method[] getMethods() : 获取所有成员方法(有权限的)
会获取到父类中非私有方法
public Method[] getDeclaredMethods() : 获取所有成员方法(包括私有)
只会获取本类中所有成员方法
(2) 获取指定成员方法并调用(无参)
// 获取成员方法
public Method getMethod(String name,
Class<?>... parameterTypes)
name : 方法名
Class<?>... parameterTypes : 指定参数列表的类型
eat() => getMethod("eat")
eat(int n) => getMethod("eat", int.class)
sleep() => getMethod("sleep")
fun(int a, String b, double c) => getMethod("fun", int.class, String.class, double.class)
// 调用成员方法
public Object invoke(Object obj,
Object... args)
obj: 要调用方法的对象
args : 调用方法需要传入的参数
- 代码演示
// 获取指定的成员方法(getName())
Method getName = aClass.getMethod("getName");
// 调用方法
System.out.println(getName.invoke(p));
(3) 获取指定成员方法并调用(有参)
// 获取指定的成员方法 (setName(String name))
Method setName = aClass.getMethod("setName", String.class);
// 调用方法
setName.invoke(p, "张三丰");
System.out.println(p);
(4) 暴力反射获取成员方法并调用
// 获取字节码对象
Class aClass = Class.forName("com.itheima._02reflect.demo02.Person");
// 获取无参构造
Constructor c = aClass.getConstructor();
// 使用无参构造, 创建对象
Person p = (Person) c.newInstance();
// 获取私有成员方法
Method eat = aClass.getDeclaredMethod("eat");
// 去除私有全新啊
eat.setAccessible(true);
// 调用
eat.invoke(p);
7. 使用反射完成榨汁机案例
- 水果类
/**
* 水果类
*/
public abstract class Fruit {
public abstract void juice();
}
- 水果的子类
public class Apple extends Fruit {
@Override
public void juice() {
System.out.println("榨出了一杯苹果汁儿~");
}
}
// ======================================================================
public class Orange extends Fruit {
@Override
public void juice() {
System.out.println("榨出了一杯橙子汁儿~");
}
}
// ==========================================================================
public class Watermelon extends Fruit {
@Override
public void juice() {
System.out.println("榨出了一杯西瓜汁儿~");
}
}
- 测试类
public class FruitTest {
@Test
public void test01() throws Exception{
// 创建Properties对象
Properties p = new Properties();
// 创建字节输入流, 关联配置文件
// FileInputStream fis = new FileInputStream("day15-code\\fruit.properties");
// 现阶段, 我们会把配置文件直接放到src下
InputStream is = FruitTest.class.getClassLoader().getResourceAsStream("fruit.properties");
// 加载配置文件中的信息
p.load(is);
// 获取classNam对应的值
String className = p.getProperty("className");
// System.out.println(className);
// 获取到字节码对象
Class aClass = Class.forName(className); // 全限定类名需要通过配置文件获取
// 获取无参构造
Constructor c = aClass.getConstructor();
// 创建对象
Fruit f = (Fruit) c.newInstance();
juicer(f);
}
/**
* 榨汁机
* @param fruit 传入的水果
*/
public void juicer(Fruit fruit) {
fruit.juice();
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)