发表于: 2017-09-04 13:26:13

2 1076


今天做的事:


记一次对于java泛型的讨论

我:话说,想问一下你java泛型,我java基础不好

大佬:是直接让我说一下泛型,还是说你有什么例子不理解

我:不不,你听我说一下,有什么不对给我点一下

大佬:请开始你的表演

我:我理解的是<>,这个符号加上后,就是个泛型了,然后在这个<>中,我们可以加上任意类型的对象(存疑),之后我们就必须使用该类型,否则编译不通过,目前是这个理解

大佬:没毛病 ,不过后面不仅是该类型 比如Integer  也可以放int,然后子类也可以

我:哦,那我定义int的时候,放Integer是不是会报错?

大佬:不可以放基本类型,必须是引用类型,子类也可以

我:是定义Integer,但是可以使用int子类

大佬:是的,这个是最基本的,还有吗?

我:然后还可以使用?这种做替代符,可以使用K,T这种做替代符,它们有什么区别

大佬:要使用KT这种,必须要在定义类的时候就添加上泛型,像这样

不然直接写一个K会被认为是无法识别的类型。

?可以直接使用代表任何类型

我:可以在定义类时不写泛型吗?

大佬:可以啊,那就用已经存在的类型,比如你有K类,那你就可以用K但是没有就不行,但是?有一个问题是如果

按照刚才截图那么写,那个list不能添加任何元素,因为?代表任何类型   1是一种具体的类型而不是任何类型,所以?不能用于在方法内部,?的作用在于传递参数的时候,或者返回值类型的时候

我:类似这种?

这个?的意思就是我懒得管你返回什么了,我也懒得写了,可以这么理解么?

大佬:是,这样写,就跟没写差不多,但是你肯定会问那要?干嘛

我:因为没有不行?

大佬:不是,没有也可以

我:那为什么?

大佬:

就在泛型中还有两个关键字,extends和super,这两个很好理解,一个是继承,一个是被继承,所以就有这种

这个代表什么我觉得你很容易就能明白

我:意思我加上这个就是为了继承

大佬:加上这个就代表返回的类型必须是Object的子类

我:那我为什么不直接写父类?一定要它的类型是子类,或者只写子类,这样也就不用写父类了啊,因为子类肯定继承父类

大佬:

比如你有一个鸭子类,鸭子接口,然后实现类有红鸭子 白鸭子等,然后你写了一个方法,这个方法既可以返回红鸭子  也可以返回白鸭子,那你返回值怎么写呢

我:哦,意思我不能返回父类,因为有些子类我不需要

大佬:而且最主要的是直接写父类就必须是父类了

这样就会报错

super也是同样de,只不过super代表的是下限

我:意思,我想返回父类的子类是不允许的,因为编译器认为我会少东西是吧

话说,问你一下super,当时学的时候理解的就一般,我只记得,使用super可以调用父类的东西,包括实例变量和方法

大佬:super就跟extends相反,x super  A就代表,x必须是A的父类

这个就是用在泛型中,Integer是Number的子类,所以可以返回带Number的泛型,跟extends一样

我:那为什么还要加上这个super Integer

大佬:代表返回类型是Integer的父类啊,如果不加,就只能返回带Integer的泛型,其他的类型都会报错

我:Integer也有很多父类吗?

大佬:Object还实现了很多接口,这些主要是用于大的项目或者工程  比如jdk本身的开发,自己写的项目可能没有这么多继承体系,那么看起来就用不到,但是用不到并不代表我们就可以不知道嘛

我:哇,真的长见识,鼠目寸光,鼠目寸光,谢谢大佬,这么认真的回答我问题,还给我写了这么多例子



节选自2017年9月4日上午某次请教



接下来就是分析Spring和Redis整合

首先看一下

RedisTemplate<K,V>

经百度查询

  • RedisTemplate是一种帮助类,可以简化Redis的访问数据的代码。在Redis数据仓库中,自动执行在给定对象与二进制数据之间的序列化和反序列化。默认情况下使用java的序列化方法(JdkSerializationRedisSerializer)来序列化对象。对于字符串的加强操作,则使用 StringRedisTemplate类。这是Redis中的核心类。


Type Parameters:

K - the Redis key type against which the template works (usually a String)

V - the Redis value type against which the template works

All Implemented Interfaces:
AwareBeanClassLoaderAwareInitializingBeanRedisOperations<K,V>

Direct Known Subclasses:
StringRedisTemplate

这是官方文档(我看的是别人解读的):https://docs.spring.io/spring-data/redis/docs/current/api/org/springframework/data/redis/core/RedisTemplate.html#discard--


然后我们定义了这个帮助类后,开始定义我们的相关方法

/**
* 新增
*<br>------------------------------<br>
* @param user
* @return
*/
public boolean add(final User user) {
boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
RedisSerializer<String> serializer = getRedisSerializer();
           byte[] key = serializer.serialize(user.getId());
           byte[] name = serializer.serialize(user.getName());
           return connection.setNX(key, name);
       }
});
   return result;
}


这个是参照例子写出来的,但是我们要弄清楚,其中的每一块

首先redisTemplate是帮助类的对象。

然后我们看到这里执行了一个execute方法,有点长,因为方法中的参数是一个new出来的带方法的类。

我们这个时候需要看一下源码

首先是execute方法

相关源码

public <T> T execute(RedisCallback<T> action) {
return this.execute(action, this.isExposeConnection());
}

public <T> T execute(RedisCallback<T> action, boolean exposeConnection) {
return this.execute(action, exposeConnection, false);
}

public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
Assert.isTrue(this.initialized, "template not initialized; call afterPropertiesSet() before using it");
   Assert.notNull(action, "Callback object must not be null");
   RedisConnectionFactory factory = this.getConnectionFactory();
   RedisConnection conn = null;

   Object var11;
   try {
if(this.enableTransactionSupport) {
conn = RedisConnectionUtils.bindConnection(factory, this.enableTransactionSupport);
       } else {
conn = RedisConnectionUtils.getConnection(factory);
       }

boolean existingConnection = TransactionSynchronizationManager.hasResource(factory);
       RedisConnection connToUse = this.preProcessConnection(conn, existingConnection);
       boolean pipelineStatus = connToUse.isPipelined();
       if(pipeline && !pipelineStatus) {
connToUse.openPipeline();
       }

RedisConnection connToExpose = exposeConnection?connToUse:this.createRedisConnectionProxy(connToUse);
       T result = action.doInRedis(connToExpose);
       if(pipeline && !pipelineStatus) {
connToUse.closePipeline();
       }

var11 = this.postProcessResult(result, connToUse, existingConnection);
   } finally {
if(!this.enableTransactionSupport) {
RedisConnectionUtils.releaseConnection(conn, factory);
       }

}

return var11;
}

可以看到其实这三个方法是同样作用的,就是最后起作用的就是第三个方法

由于源码很复杂,所以我们没必要细扣,只需要知道这个方法返回了一个对象var11

而这个值是通过

var11 = this.postProcessResult(resultconnToUseexistingConnection);

返回的,好,到此为止,不去细究了

我们只需要知道,execute方法是执行方法,它返回了一个我们需要的对象。



回到我们的add方法中,方法中参数new出来的是

new RedisCallback<Boolean>()

而且里面跟了一个方法

public Boolean doInRedis(RedisConnection connection) throws DataAccessException 

通过查看源码,我们发现是有这么一个方法

public T doInRedis(RedisConnection connection) throws DataAccessException {
if(connection.exists(this.metadata.getCacheLockKey()).booleanValue()) {
return null;
   } else {
Object var2;
       try {
connection.set(this.metadata.getCacheLockKey(), this.metadata.getCacheLockKey());
           var2 = this.doInLock(connection);
       } finally {
connection.del(new byte[][]{this.metadata.getCacheLockKey()});
       }

return var2;
   }
}


但是显然,我们是将这个方法重写了

public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
RedisSerializer<String> serializer = getRedisSerializer();
   byte[] key = serializer.serialize(user.getId());
   byte[] name = serializer.serialize(user.getName());
   return connection.setNX(key, name);
}

这里进行了序列化的操作,以及Redis存值的操作。


最后,这个方法,分析完毕,通过类似的手段,我们逐一分析其他方法。

看了一下其他的方法,都大概逐一走了一下源码,发现Spring封装的真的是,一层套一层,看的不想看,所以放弃了,大概知道每个方法大概的实现就算了。



最最后,这篇解析源码的帖子不错,虽然看不懂但还是收获:http://yychao.iteye.com/blog/1751674


这里是讲解回调机制(Callback)的帖子(很不错):

http://blog.csdn.net/xiaanming/article/details/8703708/

在测试这个回调的时候,有一个有趣的现象,就是Junit不能测试多线程

这是相应的解答

通过Junit源码,在TestRunner中可以看出,如果是单线程,当测试主线程执行结束后,不管子线程是否结束,都会回调TestResult的wasSuccessful方法,然后当Junit的主线程结束后,就会判断结果是成功还是失败,最后调用相应的System.exit()方法。大家都知道这个方法是用来结束当前正在运行中的java虚拟机,jvm都自身难保了,所以子线程也就对不住你咧...


而使用main方法就可以成功使用多线程

main方法启动后本身线程会作为守护线程存在。当程序执行完成后,守护线程退出,守护线程永远是在程序执行完成之后再退出的。

所以不会存在Junit那种子线程还没运行完,整个程序就结束了的情况


另外,还学会了一招,使程序暂停然后继续运行。

//程序暂停5秒运行
try {
Thread.sleep(5000);
}catch (Exception e){
e.printStackTrace();
}


最后,目前在实现Spring+Redis整合的@Cacheable等注解实现Redis缓存。

两篇例子

链接如下:

http://blog.csdn.net/defonds/article/details/48137969

http://blog.csdn.net/defonds/article/details/48716161


明天计划:调试成功上面的例子,并且仔细学习一下


问题:都已解决


收获:java泛型;Callback机制;Spring整合Redis分析。



返回列表 返回列表
评论

    分享到