如何使用Java管理对象方法

发布时间:2021-05-19 15:57:59 作者:Leah
来源:亿速云 阅读:140

如何使用Java管理对象方法?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

public class HelloWorld {

public void hello() {

System.out.println("hello world!");

}

}

HelloWorld helloWorld = new HelloWorld();

helloWorld.hello();

你们看,我有一个 HelloWorld 类,我用 new 就能直接创建一个对象,然后就能使用这个对象中所有的方法了,多简单啊。

二弈是工作两年的我,他一脸鄙视的对小弈说,你别整天 HelloWorld 好不好,还有啊,除了 new 你就不会其他的了,能不能有点追求啊?

小弈对二弈说那你说除了 new 还有什么办法啊?

二弈说可以通过 Class 的 newInstance 或者 Constructor 的 newInstance 来创建对象实例啊。

不过你得记住,Class 的 newInstance 只能对那些拥有可见的(Accessible)无参构造函数的类,才能进行对象的实例化,而 Constructor 就没有这些限制。

大弈是工作三年的我,他说,虽然你们的方法都可以用来创建对象,但都还是手动创建的,太原始了,生产力太低。

工欲善其事,必先利其器,我们也得找个高效的生产力工具。IOC 容器你们了解吧?

以前我们在一个对象中如果要调用另外一个对象的方法时,都是通过 new 或者反射来手动创建该对象,但是每次都这样做太累了,并且类之间的耦合也很高。

通过 IOC 容器,我们可以把所有的对象交给容器来管理,在使用之前只需要定义一下对象,然后再使用到该对象时,IOC 容器就会帮我们把该对象初始化好,这样是不是更方便呢?

大弈说完,举了一个例子:

@Bean

public class RegisterService {

public void register() {

// do register

}

}

@Bean

public class LoginService {

public void login() {

// do login

}

}

@Bean

public class HelloWorld {

@Autowired

private RegisterService registerService;

@Autowired

private LoginService loginService;

public void hello() {

// 注册

registerService.register();

// ...

// 登录

loginService.login();

}

}

IOC 容器通过一种叫 Bean 的注解,在系统启动时扫描所有通过 Bean 标注的类,对这些类进行实例化,然后将所有的对象都保存在容器中。再扫描所有通过 Autowired 标注的属性或者方法,从容器中找到与之匹配(通过名称或者类型等)的对象将具体的对象赋值给这些属性。这样我们就可以直接将这些对象拿来使用了,作为一个伸手党是不是很幸福啊。

老弈是工作五年的我,他听了大弈的话后,提出了一个问题,对于新的项目可以使用这种 IOC 的容器,可是对于那些遗留的老项目来说,要使用 IOC 来改造是不太符合实情的。

我举个例子,在一个遗留的老项目中,有一个核心的接口 Handler:

public interface Handler<REQ, RES> {

 RES handle(REQ request);

}

Handler 接口有很多的实现类,我们需要对不同的请求来调用不同的 Handler 实现类进行处理,如果用 IOC 容器来管理这些实现类,显然不太合适,因为我们处理之前是不知道该用哪个 Handler 实现类的。

大弈想了想,如果 Handler 接口只有几个固定的实现类,并且在使用时只会使用一个来进行处理,那么倒是可以在启动前通过配置的方式来确定具体使用哪种 Handler ,比如可以通过 @Conditional 根据某些条件来确定加载具体的对象,但是这种要在使用时才能确定 Handler 对象的类型确实比较棘手。

老弈看大家都不说话了,就继续说了下去。

为了要在调用方法时使用不同的 Handler 来处理不同的而请求,需要确定两种类,一种是请求类,一种是处理类,并且要让请求类和处理类一一对应起来。

假设我们的请求类是一个 Packet 类,每一个具体的请求类都继承自这个基类。

那么想要确定每一个具体的 Packet 是什么类型的,可以有很多种方法,可以为每个 Packet 取一个唯一的名字,例如:

public abstract class Packet {

  public abstract String name();

}

也可以为每一个 Packet 指定一个标志,例如:

public abstract class Packet {

  public abstract int symbol();

}

但是不管哪种方式,每一个 Packet 的实现类都需要实现抽象类中的方法,来“标志”自己是哪种 Packet。

我们以第二种方式举例,假设我们有两个具体的 Packet:

public class RegisterPacket extends Packet {

// 注册所需要的其他参数

int symbol() {

return 1;

}

}

public class LoginPacket extends Packet {

// 登录所需要的其他参数

int symbol() {

return 2;

}

}

这样当我们接收到 request 对象时,通过调用 request.symbol() 就知道这个 request 是哪种类型的 Packet 了,这时只要找到具体的 Handler 实现类来处理就可以了。

那请求类已经可以确定了,怎样确定 Handler 处理类呢?我们是否也可以在 Handler 接口中定义一个 symbol 方法呢,像这样:

public interface Handler<REQ, RES> {

  int symbol();

  RES handle(REQ request);

}

这样的话,只要在所有的实现类中实现 symbol 方法来标注该 Handler 是用来处理何种 request 的即可。

public RegisterHandler implements Handler<RegisterPacket, RES> {

  int symbol(){

  return 1;

  }

  RES handle(RegisterPacket request){

  // 具体的处理方法

  }

}

public LoginHandler implements Handler<LoginPacket, RES> {

  int symbol(){

  return 2;

  }

  RES handle(LoginPacket request){

  // 具体的处理方法

  }

}

最后把所有的 Handler 实现类都实例化后保存在一个 HandlerProvider 中,要使用时再到 HandlerProvider 中来获取即可:

public interface HandlerProvider {

  Handler getHandler(int symbol);

}

那怎样获取到所有的 Handler 的实现类呢,有两种方法。

一种是通过 ServiceLoader.load(Handler.class) 的方式来获取,不过这种通过 spi 的方式需要在项目的 resources/META-INF/services/ 目录下创建一个 xxx.Handler 的文件,并在文件中将所有 Handler 的实现类的完全类限定符列出来。

另一种比较简单的方式是通过扫描的方式,获取到所有 Handler 的实现类。

到现在为止,我们的实现还算可以,但是有一个问题,那就是在 Handler 接口中我们增加了一个方法,这样做就对原来的代码进行了侵入。

为了让原来的代码保持不变,我们可以定义一个注解来标注在所有的 Handler 实现类上,比如这样:

@Symbol(1)

public RegisterHandler implements Handler<RegisterPacket, RES> {

  RES handle(RegisterPacket request){

  // 具体的处理方法

  }

}

@Symbol(2)

public LoginHandler implements Handler<LoginPacket, RES> {

  RES handle(LoginPacket request){

  // 具体的处理方法

  }

}

这样就将 Handler 的实现和标注进行了解耦了,也可以通过扫描 @Symbol 注解来获取到所有的 Handler 实现类,不过这样做的缺点就是假如我忘记对某个 Handler 实现类添加 @Symbol 注解,到时候就获取不到该 Handler 了。

大家听完老弈的话之后,都陷入了沉思,我靠,还可以这么玩,真有趣。

这时候现在的我,也就是逅弈,说了一句,如果我有一个接口,他只有几个固定的实现类,我不想搞那一套那么重的实现方式,但是我也需要动态的获取实现类来对请求进行处理,那我该怎么办呢?

比如我有一个序列化的接口,如下所示:

public interface Serializer {

  byte[] serialize(Packet packet);

}

然后只有五种具体的序列化的实现类,如下所示:

public class JdkSerializer implements Serializer {

@Override

  public byte[] serialize(Packet packet) {

  // 具体的序列化操作

  }

}

public class FastJsonSerializer implements Serializer {

@Override

  public byte[] serialize(Packet packet) {

  // 具体的序列化操作

  }

}

public class HessianSerializer implements Serializer {

@Override

  public byte[] serialize(Packet packet) {

  // 具体的序列化操作

  }

}

public class KryoSerializer implements Serializer {

@Override

  public byte[] serialize(Packet packet) {

  // 具体的序列化操作

  }

}

public class ProtoStuffSerializer implements Serializer {

@Override

  public byte[] serialize(Packet packet) {

  // 具体的序列化操作

  }

}

那么我们该怎么确定使用哪种序列化方式对参数 packet 进行序列化呢?

使用老弈刚刚说的那一套也确实能够实现,不过太麻烦了,又得对 Packet 定义 symbol,又得对 Hander 实现类进行标注,还得扫描所有的实现类。

我只有五个实现类,不需要搞那么麻烦的。

其实很简单,只需要定义一个枚举类,表示序列化的算法,然后对 Packet 增加一个 algorithm 方法用来表示,使用何种序列化算法,如下所示:

public enum SerializeAlgorithm {

  JDK((byte) 1),

  FAST_JSON((byte) 2),

  HESSIAN((byte) 3),

  KRYO((byte) 4),

  PROTO_STUFF((byte) 5);

  private byte type;

  SerializeAlgorithm(byte type) {

    this.type = type;

  }

}

public abstract class Packet implements Serializable {

public abstract byte algorithm();

}

然后定义一个 SerializerChooser 根据不同的算法选择不同的 Serializer 实现类即可:

public interface SerializerChooser {

  Serializer choose(byte algorithm);

}

因为根据算法是可以知道对应的序列化接口的,所以就没有必要去扫描了,直接把几种序列化的实现类枚举出来即可,对象的实例可以使用单例模式,如下所示:

public class DefaultSerializerChooser implements SerializerChooser {

  private DefaultSerializerChooser() {

  }

  public static SerializerChooser getInstance() {

    return Singleton.get(DefaultSerializerChooser.class);

  }

  @Override

  public Serializer choose(byte algorithm) {

    SerializeAlgorithm serializeAlgorithm = SerializeAlgorithm.getEnum(algorithm);

    switch (serializeAlgorithm) {

      case JDK: {

        return Singleton.get(JdkSerializer.class);

      }

      case FAST_JSON: {

        return Singleton.get(FastJsonSerializer.class);

      }

      case HESSIAN: {

        return Singleton.get(HessianSerializer.class);

      }

      case KRYO: {

        return Singleton.get(KryoSerializer.class);

      }

      case PROTO_STUFF: {

        return Singleton.get(ProtoStuffSerializer.class);

      }

      default: {

        return null;

      }

    }

  }

}

java基本数据类型有哪些

Java的基本数据类型分为:1、整数类型,用来表示整数的数据类型。2、浮点类型,用来表示小数的数据类型。3、字符类型,字符类型的关键字是“char”。4、布尔类型,是表示逻辑值的基本数据类型。

关于如何使用Java管理对象方法问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。

推荐阅读:
  1. java中类方法和对象方法有什么不同
  2. 如何正确的使用HttpServletRequest对象方法

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:使用Maven怎么对Spring进行配置

下一篇:如何 在SpringBoot中注册Servlet

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》