1.类的加载概述
当程序要使用某个类时,如果该类还未被加载到内存中,
则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将.class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证 : 是否有正确的内部结构,并和其他类协调一致
准备 : 负责为类的静态成员分配内存,并设置默认初始化值
解析: 把类中的符号引用转换为直接引用
初始化: 就是我们以前讲过的初始化步骤
2:类的加载时机
创建类的实例 new Student()
访问类的静态变量,或者为静态变量赋值 Math.PI Math.class
调用类的静态方法 Math.abs()
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
类加载器的概述及其分类
1.类加载器的概述
负责将.class文件加载到内在中,并为之生成对应的Class对象。
2.类加载器的分类
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器
3。类加载器的作用
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
比如加载我们自定定义的类 Student.class Teacher.class
A:反射概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。
而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象
B:获取class文件对象的三种方式
a:Object类的getClass()方法
b:静态属性class
c:Class类中静态方法forName()
//获取.class文件的三种方法
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException {
//获取一个类的字节码文件对象的三种方式
//方式1:就是通过 Object类中的 getClass()方法
Student student = new Student();//Student.class----->字节码文件对象
Class<? extends Student> aClass = student.getClass();
Class<? extends Student> aClass1 = student.getClass();
System.out.println(aClass==aClass1);
System.out.println("==============");
//方式2:通过Class 中的静态方法 forName()方法来获取一个类的字节码文件对象
//权限定类名:包名+类名 就能确定一个类的唯一性
//使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
Class<?> student1 = Class.forName("org.westos.demo1.Student");
Class<?> student2 = Class.forName("org.westos.demo1.Student");
System.out.println(student1==student2);
Class<?> student3 = Class.forName("org.westos.demo2.Student");
System.out.println(student1==student3);
System.out.println("===================");
//方式3:针对每个类,都有一个静态的.class 属性 来获取一个 类的字节码文件对象。
Class<Student> studentClass = Student.class;
Class<Object> objectClass = Object.class;
Class<String> stringClass = String.class;
Class<String> stringClass1 = String.class;
System.out.println(stringClass==stringClass1);
}
}
一个类的构成包括:构造方法,成员变量 成员方法
通过反射来剖析这个类的三个构成部分,那么这三个构成,也会看做一种类型
构造方法 Constructor
成员变量 Field
成员方法 Method
public Constructor>[] getConstructors() 获取所有的构造方法不包含私有的
public Constructor>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
public Constructor getConstructor(Class>… parameterTypes) 获取单个的构造方法 不包含私有的
public Constructor getDeclaredConstructor(Class>… parameterTypes) 获取单个的构造方法包含私有的
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//通过反射,来调用构造方法执行,创建出该类对象
//1.先获取该类的字节码文件对象
Class<?> aClass = Class.forName("org.westos.demo2.Student");
//2.可以获取Student类的构造方法对象
//获取该类中所有的构造方法对象,私有的构造方法对象获取不到
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("===================");
//获取该类中所有的构造方法对象,包括私有的构造方法
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
System.out.println("================");
//获取单个的构造方法对象
//获取空参的构造方法对象
Constructor<?> constructor = aClass.getConstructor();
System.out.println(constructor);
System.out.println("================");
//获取一个参数的构造方法对象 参数就是,你构造方法的参数类型的字节码类型
Constructor<?> constructor1 = aClass.getConstructor(String.class);
System.out.println(constructor1);
System.out.println("====================");
//获取私有的构造方法对象 getDeclaredConstructor()
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);
System.out.println(declaredConstructor);
}
}
public class Student {
public Student() {
System.out.println("空参构造执行了");
}
public Student(String name) {
System.out.println("一个参数的构造执行了");
}
private Student(String name,int age) {
System.out.println("私有的构造执行了");
}
}
//创建构造方法
public class MyTest2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//我们通过反射的方式来创建一个类的对象。
Class<?> aClass = Class.forName("org.westos.demo2.Student");
//获取空参的构造方法对象
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
//通过构造方法对象中的方法,创建出Student类对象
Object o = declaredConstructor.newInstance();
System.out.println(o);
System.out.println("=========================");
//使用反射的方式,通过有参构造来创建对象
Constructor<?> declaredConstructor1 = aClass.getDeclaredConstructor(String.class);
Object o1 = declaredConstructor1.newInstance("李四");
System.out.println(o1);
System.out.println("===================");
//反射这种方式,即便你是私有构造,也能创建出该类的对象
//获取私有的构造方法对象
Constructor<?> declaredConstructor2 = aClass.getDeclaredConstructor(String.class, int.class);
declaredConstructor2.setAccessible(true);
Object obj = declaredConstructor2.newInstance("赵六", 26);
System.out.println(obj);
//首先获取它的字节码文件对象,再通过字节码文件对象获取构造方法对象,最后用构造方法对象中的方法创建类的对象
// Student.class----->Class ----->Constructor构造方法对象----> newInstance()
}
}
public Field[] getFields() 获取所有的成员变量包含从父类继承过来的
public Field[] getDeclaredFields() 获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量
public Field getField(String name)
public Field getDeclaredField(String name)
public class Dog {
public String name;
public int age;
private char sex;
public Dog() {
}
}
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//1.你要使用反射,第一步先获取该类的字节码文件对象。
Class<?> aClass = Class.forName("org.westos.demo4.Dog");
//2.获取Dog类中的所有字段对象,但是私有的字段对象获取不到
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("======================");
获取Dog类中的所有字段对象,包括私有的字段对象
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("=====================");
//获取单个的非私有的字段对象,你要获取哪个字段对象,你名字传入
Field fieldName = aClass.getField("name");
System.out.println(fieldName);
Field fieldAge = aClass.getField("age");
System.out.println(fieldAge);
System.out.println("=========================");
//获取单个的私有字段或非私有字段的对象
Field fieldSex = aClass.getDeclaredField("sex");
System.out.println(fieldSex);
}
}
//通过get() set()方法给成员变量设置和获取值
public class MyTest2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//我们通过反射的方式,给类中的成员变量设置值,以及获取值。
//1.你要使用反射,第一步先获取该类的字节码文件对象。
Class<?> aClass = Class.forName("org.westos.demo4.Dog");
//2.给name字段设置,我们就要获取到name字段对象
Field fieldName = aClass.getField("name");
//调用Field类中的方法来设置值
//通过反射,创建一个类的对象
Constructor<?> constructor = aClass.getConstructor();
Object o = constructor.newInstance();
//set()方法要两个参数,第一个参数,你该类的对象,第二个就是你要设置的具体的值
fieldName.set(o,"小白");
//获取name字段的值
Object o1 = fieldName.get(o);
System.out.println(o1);
System.out.println("=============");
Field fieldAge = aClass.getField("age");
fieldAge.setInt(o,100);
int anInt = fieldAge.getInt(o);
System.out.println(anInt);
System.out.println("==============");
//给私有字段设置值
Field fieldSex = aClass.getDeclaredField("sex");
fieldSex.setAccessible(true);
fieldSex.setChar(o,'女');
char aChar = fieldSex.getChar(o);
System.out.println(aChar);
//以前的方式--->new 一个对象--->对象.成员变量名
//反射的方式 Dog.class---->Class 类中的getField()--->Field 对象----通过Field 对象中的set() get()方法给字段设置值,以及获取字段的值
}
}
public Method[] getMethods() //获取所有的公共的成员方法不包含私有的 包含从父类继承过来的过来的公共方法
public Method[] getDeclaredMethods()//获取自己的所有成员方法 包含私有的
//参数1: 方法名称 参数2:方法行参的class 对象
public Method getMethod(String name,Class>… parameterTypes) //获取单个的方法 不包含私有的
public Method getDeclaredMethod(String name,Class>… parameterTypes) 获取单个方法包括私有的
public class Student {
private Student() {
}
public void hehe(){
System.out.println("呵呵");
}
public void haha(String name) {
System.out.println(name+"哈哈");
}
public void test(String name,int age) {
System.out.println(name + "test"+age);
}
public String test2(String name, int age) {
System.out.println(name + "test2" + age);
return "abc";
}
private String show(String name, int age) {
System.out.println(name + "show" + age);
return "AAA";
}
}
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//反射
//对应类中的成员方法Java提供Method这个类来描述
//1.获取该类的字节码文件对象
Class<?> aClass = Class.forName("org.westos.demo5.Student");
//2.获取类中所有的成员方法对象,获取到非私有的方法对象,包括从父类继承下来的方法对象也能够获取到
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("=========================");
//获取所有的方法对象,包括私有的,但是不获取父类的方法对象
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("=====================");
//获取单个的非私有方法对象
Method heheMethod = aClass.getMethod("hehe");
System.out.println(heheMethod);
System.out.println("=======================");
//获取有参的方法对象 getMethod()方法的参数1:方法名 参数2:你方法上形参的数据类型的Class类型
Method hahaMethod = aClass.getMethod("haha", String.class);
System.out.println(hahaMethod);
System.out.println("================");
Method testMethod = aClass.getMethod("test", String.class, int.class);
System.out.println(testMethod);
System.out.println("============================");
//获取单个的私有方法对象
Method showMethod = aClass.getDeclaredMethod("show", String.class, int.class);
System.out.println(showMethod);
}
}
//通过反射来调用方法
public class MyTest2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//现在,我们使用反射的方式,调用方法执行
Class<?> aClass = Class.forName("org.westos.demo5.Student");
//获取hehe方法对象
Method heheMethod = aClass.getMethod("hehe");
//Object invoke(Object obj, Object... args)
// 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance();
Object invoke = heheMethod.invoke(obj);
System.out.println("=======================");
//调用有参方法执行
Method hahaMethod = aClass.getMethod("haha", String.class);
hahaMethod.invoke(obj,"老师");
System.out.println("======================");
Method testMethod = aClass.getMethod("test", String.class, int.class);
testMethod.invoke(obj,"王五",25);
System.out.println("=========================");
Method test2Method = aClass.getMethod("test2", String.class, int.class);
//返回的就是test2()调用完毕之后的返回值
Object invoke1 = test2Method.invoke(obj, "赵六", 26);
System.out.println(invoke1);
System.out.println("==========================");
//调用私有的方法执行
Method showMethod = aClass.getDeclaredMethod("show", String.class, int.class);
showMethod.setAccessible(true);
Object invoke2 = showMethod.invoke(obj, "ccc", 30);
System.out.println(invoke2);
}
}
public class Cat {
public void eat(){
System.out.println("猫吃鱼");
}
}
public class Dog {
public void eat(){
System.out.println("狗吃肉");
}
}
public class MyTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Properties properties = new Properties();
properties.load(new FileReader("MyConfig.properties"));
Class<?> className = Class.forName(properties.getProperty("className"));
Object obj = className.getDeclaredConstructor().newInstance();
Method methodName = className.getDeclaredMethod(properties.getProperty("methodName"));
Object invoke = methodName.invoke(obj);
}
}
泛型 只在编译器有效,运行期就擦除了
public class MyTest {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//泛型 只在编译器有效,运行期就擦除了
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
//list.add(200);
//在指定泛型的情况下,不可以直接添加其他类型的元素
//可以通过反射机制,越过泛型检查
Class<? extends ArrayList> aClass = list.getClass();
Method addMethod = aClass.getDeclaredMethod("add", Object.class);
//调用add方法执行
addMethod.invoke(list,200);
addMethod.invoke(list,true);
System.out.println(list);
}
}
程序运行结果: aaa bbb ccc 200 true
public class Teacher {
private Teacher() {
}
private String name;
private int age;
}
public class MyUtils {
/**
*
* @param obj 你要给哪个对象设置,你把这个对象传过来
* @param propertyName 成员变量名
* @param value 给成员变量设置的具体的值
*/
public static void setProperty(Object obj,String propertyName,Object value) throws NoSuchFieldException, IllegalAccessException {
//获取字节码对象
Class<?> aClass = obj.getClass();
Field declaredField = aClass.getDeclaredField(propertyName);
declaredField.setAccessible(true);
declaredField.set(obj,value);
}
public static Object getProperty(Object obj,String propertyName) throws NoSuchFieldException, IllegalAccessException {
Class<?> aClass = obj.getClass();
Field declaredField = aClass.getDeclaredField(propertyName);
declaredField.setAccessible(true);
Object value = declaredField.get(obj);
return value;
}
}
public class MyTest {
public static void main(String[] args) throws Exception {
//通过反射写一个通用的设置某个对象的某个属性为指定的值
// Teacher teacher = new Teacher();
Class<?> aClass = Class.forName("org.westos.demo8.Teacher");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
Object obj = declaredConstructor.newInstance();
MyUtils.setProperty(obj,"name","张三");
Object name = MyUtils.getProperty(obj, "name");
System.out.println(name);
System.out.println("=====================");
MyUtils.setProperty(obj,"age",30);
Object age = MyUtils.getProperty(obj, "age");
Integer num= (Integer) age;
System.out.println(num);
}
}
动态代理概述:在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理。
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。
我们有更强大的代理cglib,Proxy类中的方法创建动态代理类对象
public static Object newProxyInstance(ClassLoader loader,Class>[] interfaces,InvocationHandler h)
最终会调用InvocationHandler的方法
InvocationHandler Object invoke(Object proxy,Method method,Object[] args)
动态代理:
* 特点:字节码随用随创建,随用随加载
* 作用:不修改源码的基础上对方法增强
* 分类:
* 基于接口的动态代理
* 基于子类的动态代理
* 基于接口的动态代理:
* 涉及的类:Proxy
* 提供者:JDK官方
* 如何创建代理对象:
* 使用Proxy类中的newProxyInstance方法
* 创建代理对象的要求:
* 被代理类最少实现一个接口,如果没有则不能使用
* newProxyInstance方法的参数:
* ClassLoader:类加载器
* 它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
* Class[]:字节码数组
* 它是用于让代理对象和被代理对象有相同方法。固定写法。
* InvocationHandler:用于提供增强的代码
* 它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类,但不是必须的。
* 此接口的实现类都是谁用谁写。
//动态代理的代码实现
public class ProxyUtils {
//传过来的是被代理对象(目标对象)
public static UserDao getProxy(UserDao dao){
/* static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。*/
//参数1:ClassLoader loader 类加载器 加载代理对象,跟目标对象用的是同一个加载器 固定写法
//参数2:接口对应的一个Class数组 代理对象需要实现的接口 固定写法
// InvocationHandler 是代理对象的调用处理程序 实现的接口。需要我们自己编写的那些增强的代码
UserDao obj = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(), dao.getClass().getInterfaces(), new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* 方法参数的含义
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//System.out.println("invoke方法调用了");
/* System.out.println("权限校验");
Object result = method.invoke(dao);//调用目标对象的方法执行。
System.out.println("日志的记录");*/
//对某个方法进行增加
Object result = null;
if (method.getName().equals("add")) {
System.out.println("权限校验");
result = method.invoke(dao);//调用目标对象的方法执行
System.out.println("日志记录");
} else {
result = method.invoke(dao);//调用目标对象的方法执行
}
return result;
}
});
return obj;
}
}
public class MyTest2 {
public static void main(String[] args) {
//代理对象:Java提供了一个Proxy类,可以帮你生成一个代理对象。
//Proxy 提供用于创建动态代理类和实例的静态方法
//创建一个代理对象的方法
/* static Object newProxyInstance (ClassLoader loader, Class < ?>[]interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
*/
UserDao userDao = new UserDaoImpl();
//获取出代理对象
UserDao proxy = ProxyUtils.getProxy(userDao);
proxy.add();
System.out.println("===============");
proxy.delete();
System.out.println("================");
proxy.update();
System.out.println("======================");
proxy.query();
}
}
枚举概述:就是一个类只能存在几个固定的对象,那么这个就是枚举.我们就可以使用这些对象可以表示一些固定的值.
举例:一周只有7天,一年只有12个月等,方向有东南西北四个
public enum Direction {
//枚举项:必须位于第一行 最后一个枚举项可以使用;分号结束
BEFORE,AFTER,LEFT,RIGHT;
String name;
//枚举里面的构造方法,必须私有,默认私有
private Direction(){
}
}
枚举的定义格式和下列自定义的枚举类相同
package org.westos.demo1;
//自定义枚举类,表示前后左右四个固定的值
// 枚举:就是一个类只能存在几个固定的对象
public class Direction {
public static final Direction BEFORE=new Direction();
public static final Direction AFTER=new Direction();
public static final Direction LEFT=new Direction();
public static final Direction RIGHT=new Direction();
private Direction() {
}
}
public class MyTest {
public static void main(String[] args) {
Direction before = Direction.BEFORE;
Direction after = Direction.AFTER;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
System.out.println(before);
System.out.println(after);
System.out.println(left);
System.out.println(right);
}
}
程序运行结果:
BEFORE
AFTER
LEFT
RIGHT
枚举类的有参构造形式
package org.westos.demo5;
// enum 枚举
// Direction 枚举类 extends Enum 但是你不要显式的写出来
public enum Direction {
//枚举项:必须位于第一行 最后一个枚举项可以使用;分号结束
BEFORE("前"),AFTER("后"),LEFT("左"),RIGHT("右");
String name;
//枚举里面的构造方法,必须私有,默认私有
private Direction(String name){
this.name=name;
}
}
上述枚举类与自定义枚举类相同:
//自定义枚举类,表示前后左右四个固定的值
// 枚举:就是一个类只能存在几个固定的对象
public class Direction {
public static final Direction BEFORE=new Direction("前");
public static final Direction AFTER=new Direction("后");
public static final Direction LEFT=new Direction("左");
public static final Direction RIGHT=new Direction("右");
public String name;
private Direction(String name) {
this.name=name;
}
}
public class MyTest {
public static void main(String[] args) {
//枚举:JDK1.5之后引入的一个概念
//自定义枚举
//方向:前后左右
Direction before = Direction.BEFORE;
Direction after = Direction.AFTER;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
System.out.println(before);
System.out.println(before.name);
System.out.println("===================");
before.show("前");
after.show("后");
left.show("左");
right.show("右");
}
}
程序运行结果:
前
后
左
右
枚举类型中加入抽象方法
public enum Direction {
//枚举项:必须位于第一行 最后一个枚举项可以使用;分号结束
BEFORE("前"){
@Override
public void show(String name) {
System.out.println(name);
}
},
AFTER("后"){
@Override
public void show(String name) {
System.out.println(name);
}
},
LEFT("左"){
@Override
public void show(String name) {
System.out.println(name);
}
},
RIGHT("右"){
@Override
public void show(String name) {
System.out.println(name);
}
};
String name;
//枚举里面的构造方法,必须私有,默认私有
private Direction(String name){
this.name=name;
}
public abstract void show(String name);
}
枚举类型中加入抽象方法与自定义枚举类中加入抽象方法相同:
//自定义枚举类,表示前后左右四个固定的值
// 枚举:就是一个类只能存在几个固定的对象
public abstract class Direction {
public static final Direction BEFORE=new Direction("前"){
@Override
public void show(String name) {
System.out.println(name);
}
};
public static final Direction AFTER=new Direction("后"){
@Override
public void show(String name) {
System.out.println(name);
}
};
public static final Direction LEFT=new Direction("左"){
@Override
public void show(String name) {
System.out.println(name);
}
};
public static final Direction RIGHT=new Direction("右"){
@Override
public void show(String name) {
System.out.println(name);
}
};
public String name;
private Direction(String name) {
this.name=name;
}
public abstract void show(String name);
}
public class MyTest {
public static void main(String[] args) {
Direction before = Direction.BEFORE;
Direction after = Direction.AFTER;
Direction left = Direction.LEFT;
Direction right = Direction.RIGHT;
System.out.println(before);
System.out.println(before.name);
System.out.println("===================");
before.show("前");
after.show("后");
left.show("左");
right.show("右");
}
}
程序运行结果:
前
后
左
右