17 反射机制
约 9946 字大约 33 分钟
2025-08-22
反射(Reflection
)的概念
反射的出现背景
Java
程序中,所有的对象都有两种类型:编译时类型和运行时类型,- 例如:
Object obj = new String("hello"); obj.getClass()
,某些变量或形参的声明类型是Object
类型,但是程序却需要调用该对象运行时类型的方法,该方法不是Object
中的方法,那么如何解决呢?
- 例如:
- 解决这个问题,有两种方案:
- 方案
1
:,在这种情况下可以先用instanceof
运算符进行判断,再利用强制类型转换符将其转换成运行时类型的变量即可 - 方案
2
:,程序只能依靠运行时信息来发现该对象和类的真实信息,这就必须
- 方案
反射概述
Reflection
()被视为动态语言的关键,Reflection API
。
加载完类之后,在堆内存的方法区中就产生了一个Class
类型的对象(Class
),这个对象包含了完整的类的结构信息,可以通过这个对象获取类的结构,这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以形象的称之为:反射。
从内存加载上看反射
Java
反射机制的应用
Java
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射相关的主要API
java.lang.Class
:代表一个类java.lang.reflect.Method
:代表类的方法java.lang.reflect.Field
:代表类的成员变量java.lang.reflect.Constructor
:代表类的构造器
反射的优缺点
优点:
- 提高了
Java
程序的灵活性和扩展性,降低了耦合性,提高自适应能力 - 允许程序创建和控制任何类的对象,无需提前硬编码目标类
- 反射会模糊程序内部逻辑,可读性较差
理解Class
类并获取Class
实例
要想剖析一个类,必须先要获取到该类的Class
对象,Class
对象是反射的根源。
要使用的相关API
如下:
java.lang.Class
java.lang.reflect.*
理解Class
在
Object
类中定义了public final Class getClass()
方法,此方法被所有子类继承,方法返回值的类型是一个Class
类,此类是Java
反射的源头。实际上所谓反射,即:可以通过对象反射求出类的名称,获取类的结构信息- 对象反射可以得到的信息:某个类的属性、方法、构造器、实现的接口等。对于每个类,
JRE
都为其保留了一个不变的Class
类型的对象。 一个Class
对象包含了特定的某个结构(class、interface、enum、annotation、primitive type、void/[]
)的有关信息 Class
Class
对象只能由系统建立对象- 一个加载的类在
JVM
中只会有一个Class
实例 - 一个
Class
对象对应的是一个加载到JVM
中的一个.class
文件 - 每个类的实例都会记得自己是由哪个
Class
实例所生成 - 通过
Class
可以完整地得到一个类中的所有被加载的结构 Class
类是Reflection
的根源,对任何想动态加载、运行的类,必须获得相应Class
对象
- 对象反射可以得到的信息:某个类的属性、方法、构造器、实现的接口等。对于每个类,
- 说明:
JDK6
JDK7
获取Class
类的实例(四种方法)
- 方式
1
:
// 前提:已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class clazz = String.class;
- 方式
2
:
// 前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class clazz = "www.atguigu.com".getClass();
- 方式
3
:
// 前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取
// 可能抛出ClassNotFoundException
Class clazz = Class.forName("java.lang.String");
- 方式
4
:
// 前提:可以用系统类加载对象或自定义加载器对象加载指定路径下的类型
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass("类的全类名");
案例代码:
public class GetClassObjTest {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1 类的class属性获取
Class c1 = GetClassObjTest.class;
// 方式2 已知某个类的实例 通过getClass()方法获取
GetClassObjTest getClassObjTest = new GetClassObjTest();
Class c2 = getClassObjTest.getClass();
// 方式3 已知全类名 并且在类路径下 通过Class.forName()获取
Class c3 = Class.forName("site.weew12.chapter17.codes.GetClassObjTest");
// 方式4 类加载器 加 全类名 获取
Class c4 = ClassLoader.getSystemClassLoader().loadClass("site.weew12.chapter17.codes.GetClassObjTest");
// c1 = class site.weew12.chapter17.codes.GetClassObjTest
System.out.println("c1 = " + c1);
// c2 = class site.weew12.chapter17.codes.GetClassObjTest
System.out.println("c2 = " + c2);
// c3 = class site.weew12.chapter17.codes.GetClassObjTest
System.out.println("c3 = " + c3);
// c4 = class site.weew12.chapter17.codes.GetClassObjTest
System.out.println("c4 = " + c4);
System.out.println();
// true
System.out.println(c1 == c2);
// true
System.out.println(c1 == c3);
// true
System.out.println(c1 == c4);
}
}
哪些类型可以有Class
对象
Java
Class
class
:外部类、成员(成员内部类、静态内部类)、局部内部类、匿名内部类interface
:接口[]
:数组enum
:枚举annotation
:注解@interface
primitive type
:基本数据类型void
案例代码:
import java.lang.annotation.ElementType;
public class TypeOfClassTest {
public static void main(String[] args) {
// Object
Class c1 = Object.class;
// c1: class java.lang.Object
System.out.println("c1: " + c1);
// 接口
Class c2 = Comparable.class;
// c2: interface java.lang.Comparable
System.out.println("c2: " + c2);
// 数组
Class c3 = String[].class;
// c3: class [Ljava.lang.String;
System.out.println("c3: " + c3);
Class c4 = int[][].class;
// c4: class [[I
System.out.println("c4: " + c4);
// 枚举 enum
Class c5 = ElementType.class;
// c5: class java.lang.annotation.ElementType
System.out.println("c5: " + c5);
// 注解
Class c6 = Override.class;
// c6: interface java.lang.Override
System.out.println("c6: " + c6);
// 基本数据类型
Class c7 = int.class;
// c7: int
System.out.println("c7: " + c7);
// void
Class c8 = void.class;
// c8: void
System.out.println("c8: " + c8);
// Class
Class c9 = Class.class;
// c9: class java.lang.Class
System.out.println("c9: " + c9);
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
// c10: class [I
System.out.println("c10: " + c10);
Class c11 = b.getClass();
// c11: class [I
System.out.println("c11: " + c11);
}
}
Class
类的常用方法
static Class forName(String name)
:返回 name
Class
Object newInstance()
:,返回该Class
对象的一个
getName()
:返回此Class
对象所表示的实体(类、接口、数组类、基本类型、void
)名称
Class[] getInterfaces()
:获取当前Class
对象的
ClassLoader getClassLoader()
:返回该类的
Class getSuperclass()
:返回表示此Class
所表示的实体的 Class
Constructor[] getConstructors()
:返回一个 Constructor
Field[] getDeclaredFields()
:返回Field
Method getMethod(String name,Class...paramTypes)
:Method
paramType
案例代码:
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ClassObjMethodsTest {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException {
// static Class forName(String name)
Class aClass = Class.forName("site.weew12.chapter17.codes.COStudent");
System.out.println("Class.forName(): " + aClass);
// Object newInstance()
Object o = aClass.newInstance();
System.out.println("newInstance(): " + o);
// getName()
String name = aClass.getName();
System.out.println("getName(): " + name);
// Class getSuperclass()
Class superclass = aClass.getSuperclass();
System.out.println("getSuperclass(): " + superclass.getName());
// Class[] getInterfaces()
Class[] interfaces = aClass.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println("getInterfaces() getName(): " + anInterface.getName());
}
// ClassLoader getClassLoader()
ClassLoader classLoader = aClass.getClassLoader();
System.out.println("getClassLoader() getName():" + classLoader.getName());
// Constructor[] getConstructors()
Constructor[] constructors = aClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.print("getConstructors() getName():" + constructor.getName());
for (Class parameterType : constructor.getParameterTypes()) {
System.out.print(parameterType.getName() + " ");
}
System.out.println();
}
// Field[] getDeclaredFields()
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("getDeclaredFields() getName():" + declaredField.getName());
}
// Method getMethod(String name,Class...paramTypes)
Method getName = aClass.getMethod("getName", null);
System.out.println("getMethod() getName():" + getName.getName());
// 输出:
// Class.forName(): class site.weew12.chapter17.codes.COStudent
// newInstance(): COStudent{name='null', age=0, sex= }
// getName(): site.weew12.chapter17.codes.COStudent
// getSuperclass(): java.lang.Object
// getInterfaces() getName(): java.io.Serializable
// getClassLoader() getName():app
// getConstructors() getName():site.weew12.chapter17.codes.COStudent
// getConstructors() getName():site.weew12.chapter17.codes.COStudentjava.lang.String int char
// getDeclaredFields() getName():name
// getDeclaredFields() getName():age
// getDeclaredFields() getName():sex
// getMethod() getName():getName
}
}
/**
* 定义一个学生类
*/
class COStudent implements Serializable {
private String name;
private int age;
private char sex;
public COStudent() {
}
public COStudent(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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 getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
@Override
public String toString() {
return "COStudent{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
}
类的加载与ClassLoader
的理解
类的生命周期
类在内存中完整的生命周期:加载-->
使用-->
卸载,: 三个阶段。
类的加载过程
当程序主动使用某个类时,。如果没有意外,JVM
将会连续完成这三个步骤,所以。
类加载的三个阶段:
1.
装载Loading
- 将类的
class
文件读入内存,为其创建一个java.lang.Class
对象,此过程由
- 将类的
2.
链接Linking
2.1
Verify
:确保加载的类信息符合JVM
规范,例如:以cafebabe
开头,没有安全方面的问题2.2
Prepare
:static
2.3
Resolve
:
3.
初始化Initialization
- 执行类构造器
_<clinit>()_
方法的过程,类构造器_<clinit>()_
方法是由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。 - 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
- 虚拟机会保证一个类的
_<clinit>()_
方法在多线程环境中被正确加锁和同步
- 执行类构造器
类加载器ClassLoader
类加载器的作用
将class
文件字节码内容加载到内存中,并 ,然后在堆中生成一个代表这个类的java.lang.Class
对象,作为方法区中类数据的访问入口。
:标准的JavaSE
类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM
垃圾回收机制可以回收这些Class
对象。
类加载器的分类(JDK8
为例)
JVM
支持两种类型的类加载器:Bootstrap ClassLoader
和 User-Defined ClassLoader
。从概念上来讲,自定义类加载器一般指的是:程序中由开发人员自定义的一类类加载器,但是Java
虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader
的类加载器都划分为自定义类加载器,无论类加载器类型如何划分,在程序中最常见的类加载器结构主要如下所示:
- 启动类加载器/引导类加载器(
Bootstrap ClassLoader
)
- 这个类加载器是使用
C/C++
语言实现的,嵌套在JVM
内部,获取它的对象时往往返回null
Java
JAVA_HOME/jre/lib/rt.jar
sun.boot.class.path
JVM
- 并不继承自
java.lang.ClassLoader
,没有父加载器 - 出于安全考虑,
Bootstrap
启动类加载器只加载包名为java、javax、sun
等开头的类 - 用于加载扩展类和应用程序类加载器,并作为他们的父类加载器
- 扩展类加载器(
Extension ClassLoader
)
Java
语言编写,由sun.misc.Launcher$ExtClassLoader
实现- 继承于
ClassLoader
类 - 父类加载器为启动类加载器
- 从
java.ext.dirs
系统属性所指定的目录中加载类库,或从JDK
的安装目录的jre/lib/ext
子目录下加载类库,如果用户创建的JAR
放在此目录下,也会自动由扩展类加载器加载
- 应用程序类加载器/系统类加载器(
AppClassLoader
)
Java
语言编写,由sun.misc.Launcher$AppClassLoader
实现- 继承于
ClassLoader
类 - 父类加载器为扩展类加载器
classpath
java.class.path
- 它是用户自定义类加载器的默认父加载器
- 通过
ClassLoader
的getSystemClassLoader()
方法可以获取到该类加载器
- 用户自定义类加载器
- 在
Java
的日常应用程序开发中,类的加载几乎是由上述3
种类加载器相互配合执行的。在必要时, Java
开发者可以自定义类加载器来实现类库的动态加载,加载源可以是本地的JAR
包,也可以是网络上的远程资源- 自定义加载器能够实现应用隔离,例如:
Tomcat
,Spring
等中间件和组件框架都在内部实现了自定义的加载器,并通过自定义加载器隔离不同的组件模块 - 自定义类加载器通常需要继承于
ClassLoader
查看某个类的类加载器对象
- 获取默认的系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// jdk.internal.loader.ClassLoaders$AppClassLoader@36baf30c
System.out.println(systemClassLoader);
- 查看某个类是哪个类加载器加载的
ClassLoader classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
// 如果是根加载器加载的类,则会得到null
ClassLoader classloader1 = Class.forName("java.lang.Object").getClassLoader();
- 获取某个类加载器的父加载器
ClassLoader parentClassloader = classloader.getParent();
案例代码(JDK21):
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException {
// 获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// systemClassLoader = jdk.internal.loader.ClassLoaders$AppClassLoader@36baf30c
System.out.println("systemClassLoader = " + systemClassLoader);
// 获取String的类加载器
ClassLoader stringClassLoader = String.class.getClassLoader();
// 加载String类的类加载器 = null
System.out.println("加载String类的类加载器 = " + stringClassLoader);
// 获取java.util.TimeZone的类加载器
ClassLoader timeZoneClassLoader = Class.forName("java.util.TimeZone").getClassLoader();
// 加载java.util.TimeZone类的类加载器 = null
System.out.println("加载java.util.TimeZone类的类加载器 = " + timeZoneClassLoader);
// 加载当前类的加载器
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
// 加载当前类的类加载器 = jdk.internal.loader.ClassLoaders$AppClassLoader@36baf30c
System.out.println("加载当前类的类加载器 = " + classLoader);
// 加载当前类的加载器的父类加载器
ClassLoader parent = classLoader.getParent();
// 加载当前类的类加载器的父加载器 = jdk.internal.loader.ClassLoaders$PlatformClassLoader@12edcd21
System.out.println("加载当前类的类加载器的父加载器 = " + parent);
// 加载当前类的加载器的父类加载器的父类加载器
ClassLoader parent1 = parent.getParent();
// 加载当前类的类加载器的父加载器的父类加载器 = null
System.out.println("加载当前类的类加载器的父加载器的父类加载器 = " + parent1);
}
}
使用ClassLoader
获取流
类加载器的一个主要方法:
getResourceAsStream(String str)
:获取类路径下的指定文件的输入流
案例代码:
info1.properties
文件内容(Maven 项目的话放到 resources 目录下,否则编译构建不会输出这个资源文件)
name=weew12
password=123456
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class ClassLoaderGetResourcesAsStreamTest {
public static void main(String[] args) throws IOException {
ClassLoader classLoader = ClassLoaderGetResourcesAsStreamTest.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("info1.properties");
Properties properties = new Properties();
properties.load(resourceAsStream);
Object name = properties.get("name");
String password = properties.getProperty("password");
System.out.println(name);
System.out.println(password);
}
}
反射的基本应用
创建运行时类的对象
这是反射机制应用最多的地方,创建运行时类的对象有两种方式:
- 方式
1
:Class
newInstance()
- 要求:
1.
2.
- 步骤:
1.
获取该类型的Class
对象2.
调用Class
对象的newInstance()
方法创建对象
- 要求:
- 方式
2
:通过获取构造器对象来进行实例化- 步骤:
1.
通过Class
类的getDeclaredConstructor(Class... parameterTypes)
取得本类的指定形参类型的构造器2.
向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数3.
通过Constructor
实例化对象
- 步骤:
setAccessible(true)
案例代码:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectionDemo1 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 方式1 newInstance() 调用有访问权限的无参构造函数
Class aClass = Class.forName("site.weew12.chapter17.codes.RDemo");
Object o = aClass.newInstance();
// site.weew12.chapter17.codes.RDemo@6e8cf4c6
System.out.println(o);
// 方式2 获取构造器对象进行实例化
Class aClass1 = Class.forName("site.weew12.chapter17.codes.RDemo");
Constructor constructor = aClass1.getConstructor(String.class);
Object hello = constructor.newInstance("hello");
// site.weew12.chapter17.codes.RDemo@34c45dca
System.out.println(hello);
}
}
/**
* 测试类
*/
class RDemo {
private String info;
public RDemo() {
}
public RDemo(String info) {
this.info = info;
}
}
获取运行时类的完整结构
:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)
相关API
1.
实现的全部接口public Class<?>[] getInterfaces()
:确定此对象所表示的类或接口
2.
所继承的父类public Class<? Super T> getSuperclass()
:返回表示此Class
所表示的实体(类、接口、基本类型)的Class
3.
全部的构造器public Constructor<T>[] getConstructors()
:返回此Class
对象所表示的类的所有public
public Constructor<T>[] getDeclaredConstructors()
:返回此Class
对象表示的类声明的Constructor
类的方法:public int getModifiers();
public String getName();
public Class<?>[] getParameterTypes();
4.
全部的方法public Method[] getDeclaredMethods()
:返回Class
对象所表示的类或接口的public Method[] getMethods()
:返回此Class
对象所表示的类或接口的public
Method
类的方法:public Class<?> getReturnType()
public Class<?>[] getParameterTypes()
public int getModifiers()
public Class<?>[] getExceptionTypes()
5.
全部的Field
public Field[] getFields()
:返回Class
对象所表示的类或接口的public
Field
public Field[] getDeclaredFields()
:返回Class
对象所表示类或接口的Field
Field
类的方法:Field
public int getModifiers()
Field
public Class<?> getType()
Field
public String getName()
6.
Annotation
相关getAnnotation(Class<T> annotationClass)
:如果存在同名的AnnotationElement
,则返回指定类型的第一个注解,否则返回null
getDeclaredAnnotations()
:0
7.
泛型相关Type getGenericSuperclass()
:获取ParameterizedType
:泛型类型getActualTypeArguments()
:
8.
类所在的包Package getPackage()
获取所有的属性及相关细节
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class ReflectionDemo2FiledMethods {
public static void main(String[] args) {
// 0. 获取Class对象
Class<R2FieldExample> r2FieldExampleClass = R2FieldExample.class;
// 1. getFields() 获取运行时类本身及其所有父类中声明为public权限的属性
Field[] fields = r2FieldExampleClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println();
// 输出:
// public java.lang.String site.weew12.chapter17.codes.R2FieldExample.publicField
// public static java.lang.String site.weew12.chapter17.codes.R2FieldExample.staticPublicField
// 2. getDeclaredFields() 获取当前运行时类中声明的所有属性
Field[] declaredFields = r2FieldExampleClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println();
// 输出:
// public java.lang.String site.weew12.chapter17.codes.R2FieldExample.publicField
// protected java.lang.String site.weew12.chapter17.codes.R2FieldExample.protectedField
// private final java.lang.String site.weew12.chapter17.codes.R2FieldExample.privateField
// public static java.lang.String site.weew12.chapter17.codes.R2FieldExample.staticPublicField
// private static final java.lang.String site.weew12.chapter17.codes.R2FieldExample.STATIC_PRIVATE_FIELD
// 3. 获取 权限修饰符 变量类型 变量名
Field[] declaredFields1 = r2FieldExampleClass.getDeclaredFields();
for (Field field : declaredFields1) {
// 3.1 权限修饰符
/*
* 0x是十六进制
* PUBLIC = 0x00000001; 1 00001 (二进制)
* PRIVATE = 0x00000002; 2 00010 (二进制)
* PROTECTED = 0x00000004; 4 00100 (二进制)
* STATIC = 0x00000008; 8 01000 (二进制)
* FINAL = 0x00000010; 16 10000 (二进制)
* ...
* 设计的理念,就是用二进制的某一位是 1,来代表一种修饰符,整个二进制中只有一位是 1,其余都是 0
* mod = 17 = 0x00000011 17 10001 (二进制)
* if ((mod & PUBLIC) != 0) 说明修饰符中有public 00001 & 10001 = 00001
* if ((mod & FINAL) != 0) 说明修饰符中有final 10000 & 10001 = 10000
*/
int modifiers = field.getModifiers();
System.out.printf("%25s\t", Modifier.toString(modifiers));
// 3.2 数据类型
Class<?> type = field.getType();
System.out.printf("%25s\t", type.getName());
// 3.3 变量名
String name = field.getName();
System.out.printf("%25s\t", name);
System.out.println();
}
System.out.println();
// 输出:
// public java.lang.String publicField
// protected java.lang.String protectedField
// private final java.lang.String privateField
// public static java.lang.String staticPublicField
// private static final java.lang.String STATIC_PRIVATE_FIELD
}
}
class R2FieldExample {
/**
* 公共字段
*/
public String publicField = "I am a public field";
/**
* 受保护字段
*/
protected String protectedField = "I am a protected field";
/**
* 私有字段
*/
private final String privateField = "I am a private field";
/**
* 静态公共字段
*/
public static String staticPublicField = "I am a static public field";
/**
* 静态私有字段
*/
private static final String STATIC_PRIVATE_FIELD = "I am a static private field";
}
获取所有的方法及相关细节
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class ReflectionDemo2MethodMethods {
public static void main(String[] args) {
// 0. 获取Class对象
Class<MethodExample> methodExampleClass = MethodExample.class;
// 1. getMethods() 获取运行时类本身及其所有父类声明为pubic权限的方法
Method[] methods = methodExampleClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println();
// 输出:
// public void site.weew12.chapter17.codes.MethodExample.publicMethod()
// public static void site.weew12.chapter17.codes.MethodExample.staticPublicMethod()
// public void site.weew12.chapter17.codes.MethodExampleParent.publicMethodParent()
// public static void site.weew12.chapter17.codes.MethodExampleParent.staticPublicMethodParent()
// public boolean java.lang.Object.equals(java.lang.Object)
// public java.lang.String java.lang.Object.toString()
// public native int java.lang.Object.hashCode()
// public final native java.lang.Class java.lang.Object.getClass()
// public final native void java.lang.Object.notify()
// public final native void java.lang.Object.notifyAll()
// public final void java.lang.Object.wait(long) throws java.lang.InterruptedException
// public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
// public final void java.lang.Object.wait() throws java.lang.InterruptedException
// 2. getDeclaredMethods() 获取运行时类中声明的所有方法
Method[] declaredMethods = methodExampleClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println();
// 输出:
// public void site.weew12.chapter17.codes.MethodExample.publicMethod()
// private void site.weew12.chapter17.codes.MethodExample.privateMethod()
// public static void site.weew12.chapter17.codes.MethodExample.staticPublicMethod()
// protected void site.weew12.chapter17.codes.MethodExample.protectedMethod()
// private static void site.weew12.chapter17.codes.MethodExample.staticPrivateMethod()
// 3. 注解信息 权限修饰符 返回值类型 方法名(形参类型1 参数1, 形参类型2 参数2, ...) throws 异常类型1,... {}
Method[] declaredMethods1 = methodExampleClass.getDeclaredMethods();
for (Method method : declaredMethods1) {
// 3.1 获取注解信息
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
// 3.2 获取权限修饰符
System.out.print(Modifier.toString(method.getModifiers()) + " ");
// 3.3 获取返回值类型
System.out.print(method.getReturnType().getName() + " ");
// 3.4 获取方法名
System.out.print(method.getName());
// 3.5 获取参数列表
System.out.print("(");
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes != null || parameterTypes.length != 0) {
for (int i = 0; i < parameterTypes.length; i++) {
System.out.print(parameterTypes[i].getName() + " arg_" + (i + 1));
if (i < parameterTypes.length) {
System.out.print(", ");
}
}
}
System.out.print(")");
// 3.6 获取抛出的异常
Class<?>[] exceptionTypes = method.getExceptionTypes();
if (exceptionTypes.length > 0) {
System.out.print(" throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
System.out.print(exceptionTypes[i].getName());
if (i < exceptionTypes.length) {
System.out.print(", ");
}
}
}
System.out.println();
}
System.out.println();
// 输出:
// public static void staticPublicMethod()
// protected void protectedMethod() throws java.lang.RuntimeException,
// private void privateMethod() throws java.lang.IndexOutOfBoundsException,
// public void publicMethod() throws java.io.IOException,
// private static void staticPrivateMethod()
}
}
class MethodExampleParent {
/**
* 公共方法
*/
public void publicMethodParent() {
System.out.println("I am a public methodParent");
}
/**
* 受保护方法
*/
protected void protectedMethodParent() {
System.out.println("I am a protected methodParent");
}
/**
* 私有方法
*/
private void privateMethodParent() {
System.out.println("I am a private methodParent");
}
/**
* 静态公共方法
*/
public static void staticPublicMethodParent() {
System.out.println("I am a static public methodParent");
}
/**
* 静态私有方法
*/
private static void staticPrivateMethodParent() {
System.out.println("I am a static private methodParent");
}
}
class MethodExample extends MethodExampleParent {
/**
* 公共方法
*/
public void publicMethod() throws IOException {
System.out.println("I am a public method");
}
/**
* 受保护方法
*/
protected void protectedMethod() throws RuntimeException {
System.out.println("I am a protected method");
}
/**
* 私有方法
*/
private void privateMethod() throws IndexOutOfBoundsException {
System.out.println("I am a private method");
}
/**
* 静态公共方法
*/
public static void staticPublicMethod() {
System.out.println("I am a static public method");
}
/**
* 静态私有方法
*/
private static void staticPrivateMethod() {
System.out.println("I am a static private method");
}
}
获取其他结构(构造器、父类、接口、包、注解等)
package site.weew12.chapter17.codes;
import java.beans.JavaBean;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
public class ReflectionDemo3Other {
public static void main(String[] args) {
// 0. 获取Class对象
Class<R3MethodExample> r3MethodExampleClass = R3MethodExample.class;
// 1. 获取运行时类的所有构造器
Constructor<?>[] constructors = r3MethodExampleClass.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println();
// 输出:
// public site.weew12.chapter17.codes.R3MethodExample()
// public site.weew12.chapter17.codes.R3MethodExample(java.lang.String,java.lang.Integer,java.lang.String)
// 2. 获取运行时类的父类
Class<? super R3MethodExample> superclass = r3MethodExampleClass.getSuperclass();
System.out.println(superclass);
System.out.println();
// 输出:
// class site.weew12.chapter17.codes.R3MethodExampleParentGeneric
// 3. 获取运行时类所在的包
Package aPackage = r3MethodExampleClass.getPackage();
System.out.println(aPackage);
System.out.println();
// 输出:
// package site.weew12.chapter17.codes
// 4. 获取运行时类的注解
Annotation[] annotations = r3MethodExampleClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
System.out.println();
// 输出:
// @java.lang.Deprecated(forRemoval=false, since="")
// @java.beans.JavaBean(defaultProperty="", description="", defaultEventSet="")
// 5. 获取运行时类所实现的接口
Class<?>[] interfaces = r3MethodExampleClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
System.out.println();
// 输出:
// interface java.io.Serializable
// 6. 获取运行时类的带泛型的父类
Type genericSuperclass = r3MethodExampleClass.getGenericSuperclass();
System.out.println(genericSuperclass);
System.out.println();
// 输出:
// site.weew12.chapter17.codes.R3MethodExampleParentGeneric<T>
}
}
class R3MethodExampleParentGeneric<T> {
}
@Deprecated
@JavaBean
class R3MethodExample<T> extends R3MethodExampleParentGeneric<T> implements Serializable {
private String name;
Integer age;
public String nickName;
public R3MethodExample() {
}
public R3MethodExample(String name, Integer age, String nickName) {
this.name = name;
this.age = age;
this.nickName = nickName;
}
}
获取泛型父类信息
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class ReflectionDemo4Generic {
/**
* 需求:运行时 获取R4Son的泛型父类的泛型参数 < String, Integer >
*/
public static void main(String[] args) {
// 1. 获取Class对象
Class<R4Son> r4SonClass = R4Son.class;
// 2. 获取泛型父类
// getSuperclass()只能得到父类名,无法得到父类的泛型实参列表
// Class<? super R4Son> superclass = r4SonClass.getSuperclass();
// getGenericSuperclass() 返回Type
Type genericSuperclass = r4SonClass.getGenericSuperclass();
ParameterizedType genericSuperclass1 = (ParameterizedType) genericSuperclass;
// 3. 获取泛型父类的泛型参数列表
Type[] actualTypeArguments = genericSuperclass1.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
System.out.println();
// 输出:
// class java.lang.String
// class java.lang.Integer
}
}
/**
* 泛型父类
*/
class R4Father<T, U> {
}
/**
* 继承泛型父类 设置泛型实参 String Integer
*/
class R4Son extends R4Father<String, Integer> {
}
获取内部类或外部类信息
public Class<?>[] getClasses()
:返回所有公共内部类和内部接口,包括从超类继承的公共类和接口成员以及该类声明的公共类和接口成员public Class<?>[] getDeclaredClasses()
:返回Class
对象的一个数组,代表此Class
对象所表示的类的成员的所有类和接口,包括该类所声明的公共、保护、默认(包)访问及私有类和接口,但public Class<?> getDeclaringClass()
:Class
null
Class<?> getEnclosingClass()
:
public class ReflectionDemo5InerClass {
public static void main(String[] args) throws ClassNotFoundException {
// 0. 获取Class对象
Class<R5ClassExample> r5ClassExampleClass = R5ClassExample.class;
// 1. getClasses() 获取所有公共内部类和内部接口 包括继承的公共权限的
Class<?>[] classes = r5ClassExampleClass.getClasses();
for (Class<?> aClass : classes) {
System.out.println(aClass);
}
System.out.println();
// 输出:
// interface site.weew12.chapter17.codes.R5ClassExample$PublicInnerInterface
// class site.weew12.chapter17.codes.R5ClassExample$StaticPublicInnerClass
// class site.weew12.chapter17.codes.R5ClassExample$PublicInnerClass
// 2. getDeclaredClasses() 返回Class对象表示的类的成员的所有类和接口 不包括继承的
Class<?>[] declaredClasses = r5ClassExampleClass.getDeclaredClasses();
for (Class<?> declaredClass : declaredClasses) {
System.out.println(declaredClass);
}
System.out.println();
// 输出:
// interface site.weew12.chapter17.codes.R5ClassExample$PrivateInnerInterface
// interface site.weew12.chapter17.codes.R5ClassExample$ProtectedInnerInterface
// interface site.weew12.chapter17.codes.R5ClassExample$PublicInnerInterface
// class site.weew12.chapter17.codes.R5ClassExample$StaticPrivateInnerClass
// class site.weew12.chapter17.codes.R5ClassExample$StaticPublicInnerClass
// class site.weew12.chapter17.codes.R5ClassExample$PrivateInnerClass
// class site.weew12.chapter17.codes.R5ClassExample$ProtectedInnerClass
// class site.weew12.chapter17.codes.R5ClassExample$PublicInnerClass
// 3. getDeclaringClass() 如果此Class对象所表示的类或接口是一个内部类或内部接口,则返回它的外部类或外部接口
Class<?> declaringClass = r5ClassExampleClass.getDeclaringClass();
System.out.println(declaringClass);
Class<?> aClass = Class.forName("site.weew12.chapter17.codes.R5ClassExample$PublicInnerClass");
Class<?> declaringClass1 = aClass.getDeclaringClass();
System.out.println(declaringClass1);
System.out.println();
// 输出:
// null
// class site.weew12.chapter17.codes.R5ClassExample
// 4. getEnclosingClass() 返回某个内部类的外部类
Class<?> enclosingClass = aClass.getEnclosingClass();
System.out.println(enclosingClass);
System.out.println();
// 输出:
// class site.weew12.chapter17.codes.R5ClassExample
}
}
class R5ClassExample {
/**
* 公共内部类
*/
public class PublicInnerClass {
public void publicInnerMethod() {
System.out.println("I am a public inner method");
}
}
/**
* 受保护内部类
*/
protected class ProtectedInnerClass {
protected void protectedInnerMethod() {
System.out.println("I am a protected inner method");
}
}
/**
* 私有内部类
*/
private class PrivateInnerClass {
private void privateInnerMethod() {
System.out.println("I am a private inner method");
}
}
/**
* 静态公共内部类
*/
public static class StaticPublicInnerClass {
public static void staticPublicInnerMethod() {
System.out.println("I am a static public inner method");
}
}
/**
* 静态私有内部类
*/
private static class StaticPrivateInnerClass {
private static void staticPrivateInnerMethod() {
System.out.println("I am a static private inner method");
}
}
/**
* 公共内部接口
*/
public interface PublicInnerInterface {
void publicInnerInterfaceMethod();
}
/**
* 受保护内部接口
*/
protected interface ProtectedInnerInterface {
void protectedInnerInterfaceMethod();
}
/**
* 私有内部接口
*/
private interface PrivateInnerInterface {
void privateInnerInterfaceMethod();
}
}
小结
- 在实际的操作中,取得类的信息的操作代码,并不会经常开发
- 一定要熟悉
java.lang.reflect
包的作用,反射机制
调用运行时类的指定结构
调用指定的属性
在反射机制中,可以直接通过Field
类操作类中的属性,通过Field
类提供的set()
和get()
方法就可以完成设置和取得属性内容的操作。
- 获取该类型的
Class
对象
Class clazz = Class.forName("包.类名");
- 获取属性对象
Field field = clazz.getDeclaredField("属性名");
public
field.setAccessible(true);
- 关于
setAccessible
方法的使用:Method
、Field
、Constructor
对象都有setAccessible()
方法setAccessible
用于启动和禁用访问安全检查的开关- 参数值为
true
则指示反射的对象在使用时应该取消Java
语言访问检查- 提高反射的效率,如果代码中必须用反射,而该句代码需要频繁的被调用,那么设置
true
- 使得原本无法访问的私有成员也可以访问
- 提高反射的效率,如果代码中必须用反射,而该句代码需要频繁的被调用,那么设置
- 参数值为
false
则指示反射的对象应该实施Java
语言访问检查
- 创建实例对象:如果操作的是非静态属性,需要创建实例对象
// 有公共的无参构造
Object obj = clazz.newInstance();
// 通过特定构造器对象创建实例对象
Object obj = 构造器对象.newInstance(实参...);
- 设置指定对象
obj
上Field
的属性内容
field.set(obj,"属性值");
// 如果操作静态变量,那么实例对象可以省略,用null表示
- 取得指定对象
obj
上Field
的属性内容
Object value = field.get(obj);
// 如果操作静态变量,那么实例对象可以省略,用null表示
案例代码:
import java.lang.reflect.Field;
public class ReflectionDemo6ClassFiled {
public static void main(String[] args) throws NoSuchFieldException, InstantiationException, IllegalAccessException {
// 0. 获取Class对象
Class<R6Student> r6StudentClass = R6Student.class;
// 1. 获取属性对象
Field name = r6StudentClass.getDeclaredField("name");
// 2. 如果属性是私有的不可访问 可以通过setAccessible(true)修改访问权限
name.setAccessible(true);
// 3. 创建实例对象
R6Student r6Student = r6StudentClass.newInstance();
// 4. 获取属性值
String originName = r6Student.getName();
System.out.println("origin name value = " + originName);
// 输出:
// origin name value = null
// 5. 设置属性值
name.set(r6Student, "weew12");
String newName = r6Student.getName();
System.out.println("new name value = " + newName);
// 输出:
// new name value = weew12
}
}
class R6Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "R6Student{" +
"name='" + name + '\'' +
'}';
}
}
调用指定的方法
- 获取该类型的
Class
对象
Class clazz = Class.forName("包.类名");
- 获取方法对象
Method method = clazz.getDeclaredMethod("方法名",方法的形参类型列表);
- 创建实例对象
Object obj = clazz.newInstance();
- 调用方法
Object result = method.invoke(obj, 方法的实参值列表);
// 如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)
// 如果方法是静态方法,实例对象也可以省略,用null代替
案例代码:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionDemo7ClassMethodInvoke {
public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
// 0. 获取Class对象
Class<R7Student> r7StudentClass = R7Student.class;
// 1. 获取方法对象
/*
* 在一个类中 唯一定位一个方法需要:
* 1. 方法名
* 2. 方法形参列表 (因为方法可能重载)
* 例如:
* void setName(String name)
*/
Method setName = r7StudentClass.getDeclaredMethod("setName", String.class);
Method getName = r7StudentClass.getDeclaredMethod("getName", null);
// 2. 创建实例对象
R7Student r7Student = r7StudentClass.newInstance();
// 3. 调用方法
/*
* 以前:学生对象.setName(值)
* 现在:方法对象.invoke(学生对象, 值)
*/
Object setNameReturn = setName.invoke(r7Student, "weew12");
// 输出:R7Student r7Student = R6Student{name='weew12'}
System.out.println("R7Student r7Student = " + r7Student);
// 输出:setName invoke调用 返回值 setNameReturn = null
System.out.println("setName invoke调用 返回值 setNameReturn = " + setNameReturn);
Object getNameReturn = getName.invoke(r7Student, null);
// 输出:getName invoke调用 返回值 getNameReturn = weew12
System.out.println("getName invoke调用 返回值 getNameReturn = " + getNameReturn);
// 4. 调用静态方法
Method staticMethod = r7StudentClass.getMethod("staticMethod", String.class);
// 输出:this is static method invoke, msg: test...
Object staticMethodReturn = staticMethod.invoke(null, "test...");
// 输出:staticMethod invoke调用 返回值 staticMethodReturn = null
System.out.println("staticMethod invoke调用 返回值 staticMethodReturn = " + staticMethodReturn);
}
}
class R7Student {
private String name;
public String getName() {
return name;
}
/**
* 成员方法
*
* @param name 姓名
*/
public void setName(String name) {
this.name = name;
}
/**
* 静态方法
*/
public static void staticMethod(String msg) {
System.out.println("this is static method invoke, msg: " + msg);
}
@Override
public String toString() {
return "R6Student{" + "name='" + name + '\'' + '}';
}
}
getResourceAsStream()
在Java中,
Class.getResourceAsStream()
和ClassLoader.getResourceAsStream()
都可以用来加载资源文件,但它们的工作方式略有不同
Class.getResourceAsStream()
Class.getResourceAsStream(String path)
path
- 如果
path
以/
开头,则表示绝对路径,从类的根路径开始查找- 如果
path
不以/
开头,则表示相对路径,从类的包路径开始查找
ClassLoader.getResourceAsStream()
ClassLoader.getResourceAsStream(String name)
name
name
/
练习
读取
user.properties
文件中的数据,通过反射完成User
类对象的创建及对应方法的调用。
user.properties
配置文件
className:site.weew12.chapter17.codes.R7User
methodName:show
案例代码:
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectionDemo7OpByFileInfo {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 1. 创建Properties对象
Properties properties = new Properties();
// 2. 加载配置文件信息 转换为Properties
Class<ReflectionDemo7OpByFileInfo> reflectionDemo7OpByFileInfoClass = ReflectionDemo7OpByFileInfo.class;
InputStream resourceAsStream = reflectionDemo7OpByFileInfoClass.getResourceAsStream("/user.properties");
properties.load(resourceAsStream);
// 3. 获取配置文件中定义的数据
Object className = properties.get("className");
Object methodName = properties.get("methodName");
// 4. 获取Class对象
Class<?> targetClass = Class.forName((String) className);
// 5. 创建对象
Object instance = targetClass.newInstance();
// 6. 获取指定的方法对象
Method method = targetClass.getMethod((String) methodName);
// 7. 调用方法
Object invokeReturn = method.invoke(instance);
System.out.println("method.invoke(instance) return result = " + invokeReturn);
}
}
class R7User {
private String name;
public R7User() {
}
public R7User(String name) {
this.name = name;
}
public void show() {
System.out.println("invoke R7User show method...");
}
}
读取注解信息
一个完整的注解应该包含三个部分:1.
声明2.
使用3.
读取
声明自定义注解
import java.lang.annotation.*;
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
/**
* 表名
*/
String value();
}
import java.lang.annotation.*;
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
/**
* 列名
*/
String columnName();
/**
* 列类型
*/
String columnType();
}
- 自定义注解可以通过四个元注解:
@Retention
:生命周期RetentionPolicy.SOURCE
RetentionPolicy.CLASS
RetentionPolicy.RUNTIME
@Target
:使用位置(具体查文档:docs/api/java.base/java/lang/annotation/ElementType.html
)@Inherited
:是否被继承@Documented
:是否被生成到API文档中
- ,又称为 。
String
Class
enum
Annotation
- 可以使用
default
关键字为抽象方法指定默认返回值 - 如果定义的注解含有抽象方法,那么使用时必须指定返回值,除非它有默认值
- 格式:
方法名=返回值
,如只有一个抽象方法需赋值,且方法名为value
,可省略value=
,所以如果注解只有一个抽象方法成员,建议使用方法名value
,案例如下:
- 格式:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomAnnotation {
String value() default "";
}
使用自定义注解
@Table("t_stu")
public class Student {
@Column(columnName = "id", columnType = "int")
private int id;
@Column(columnName = "name", columnType = "varchar(20)")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
读取和处理自定义注解
,自己定义的注解只能使用反射的代码读取,所以自定义注解的生命周期必须是RetentionPolicy.RUNTIME
。
import java.lang.reflect.Field;
public class AnnotationTest {
public static void main(String[] args) {
// 0. 获取Class对象
Class<Student> studentClass = Student.class;
// 1. 通过Table注解读取表名字设置
Table tableAnnotation = studentClass.getAnnotation(Table.class);
String tableName = "";
if (tableAnnotation != null) {
tableName = tableAnnotation.value();
}
// 2. 通过Column注解读取列名设置
Field[] declaredFields = studentClass.getDeclaredFields();
String[] columns = new String[declaredFields.length];
int index = 0;
for (Field declaredField : declaredFields) {
Column columnAnnotation = declaredField.getAnnotation(Column.class);
if (columnAnnotation != null) {
columns[index++] = columnAnnotation.columnName();
}
}
// 3. 模拟拼接sql查询字符串
StringBuilder sql = new StringBuilder("select ");
for (int i = 0; i < index; i++) {
sql.append(columns[i]);
if (i < index - 1) {
sql.append(",");
}
}
sql.append(" from ").append(tableName);
// 4. 拼接结果
// 输出:sql = select id,name from t_stu
System.out.println("sql = " + sql);
}
}
体会反射的动态性
- 动态的创建给定字符串对应的类的对象
- 动态的创建指定字符串对应类的对象,并调用指定的方法
- 加载配置文件,根据配置动态创建对应类的实例并执行方法(结合多态调用具体的方法)
3
config.properties
fruitName=site.weew12.chapter17.codes.Apple
#fruitName=site.weew12.chapter17.codes.Orange
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.util.Properties;
public class ReflectionDemo8Dynamic {
public static void main(String[] args) throws Exception {
// 1. 加载配置文件
Properties properties = new Properties();
InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("config.properties");
properties.load(resourceAsStream);
// 2. 创建对应的类实例
String fruitName = properties.getProperty("fruitName");
Class<?> aClass = Class.forName(fruitName);
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Fruit fruit = (Fruit) declaredConstructor.newInstance();
// 3. 调用方法
Juicer juicer = new Juicer();
juicer.run(fruit);
}
}
/**
* 定义水果接口 实现多态
*/
interface Fruit {
/**
* 榨汁
*/
void squeeze();
}
/**
* 苹果
*/
class Apple implements Fruit {
@Override
public void squeeze() {
System.out.println("榨出一杯苹果汁...");
}
}
/**
* 橘子
*/
class Orange implements Fruit {
@Override
public void squeeze() {
System.out.println("榨出一杯橙汁...");
}
}
/**
* 榨汁机
*/
class Juicer {
public void run(Fruit f) {
f.squeeze();
}
}