一、成员变量
? ? 在Java中让我们去区分变量的类型是件轻而易举的事情,无非就是成员变量作用域为整个类、局部变量作用域就是方法内部及变量所在处以下。我们常常忙于业务工作,知其然而不知其所以然。今天我们来聊聊成员变量个人理解的一些作用,希望能够对你有所帮助。
二、成员变量的作用源码为例
成员变量在源码里通常都是通过构造方法初始化(有时setter、直接初始化等),变量顾名思义就是可变的量,个人认为成员变量的作用有:条件、数据装载(多见集合)、工具(辅助完成功能)、变更数据、填充等等,当然有时它会充当多种作用。
1、条件、工具作用
以MyBatis的GenericTokenParser的3个成员变量为例
/**
* @sparkle StringBuilder、被SqlSourceBuilder等封装、、、、
*
* @description 普通记号解析器,处理#{}和${}参数和其它占位符
* @author Clinton Begin
*
* 关于成员变量的作用:条件、填充、工具、数据装载
*/
public class GenericTokenParser {
// 有一个开始和结束记号
private final String openToken; // 功能性条件变量
private final String closeToken; // 功能性条件变量
// 记号处理器
private final TokenHandler handler; // 这里是一种工具作用,辅助完成解析功能,同时它是一个接口类型
// 公有构造方法,一般在外部构造初始化,为什么要提供一个多参构造方法:因为你要初始化成员变量
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
public String parse(String text) {
StringBuilder builder = new StringBuilder();
if (text != null && text.length() > 0) {
char[] src = text.toCharArray();
int offset = 0;
int start = text.indexOf(openToken, offset);
// #{favouriteSection,jdbcType=VARCHAR}
// 这里是循环解析参数,参考GenericTokenParserTest,比如可以解析${first_name} ${initial} ${last_name} reporting.这样的字符串,里面有3个 ${}
while (start > -1) {
// 判断一下 ${ 前面是否是反斜杠,这个逻辑在老版的mybatis中(如3.1.0)是没有的
if (start > 0 && src[start - 1] == '\\') {
// the variable is escaped. remove the backslash.
// 新版已经没有调用substring了,改为调用如下的offset方式,提高了效率
// issue #760
builder.append(src, offset, start - offset - 1).append(openToken);
offset = start + openToken.length();
} else {
int end = text.indexOf(closeToken, start);
if (end == -1) {
builder.append(src, offset, src.length - offset);
offset = src.length;
} else {
builder.append(src, offset, start - offset);
offset = start + openToken.length();
String content = new String(src, offset, end - offset);
// 得到一对大括号里的字符串后,调用handler.handleToken,比如替换变量这种功能
builder.append(handler.handleToken(content));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
}
return builder.toString();
}
}
2、数据装载、数据存取
以MyBatis的DefaultReflectorFactory为例
/**
* 默认反射工厂,具有缓存功能(底层是ConcurrentHashMap)
*/
public class DefaultReflectorFactory implements ReflectorFactory {
// 是否开启缓存,默认开启
private boolean classCacheEnabled = true;
// 成员变量reflectorMap,生命周期比较长,故可以用作缓存,存取Reflector
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
public DefaultReflectorFactory() {
}
@Override
public boolean isClassCacheEnabled() {
return classCacheEnabled;
}
/**
* 提供更改是否开启缓存方法
*
* @param classCacheEnabled 是否开启缓存
*/
@Override
public void setClassCacheEnabled(boolean classCacheEnabled) {
this.classCacheEnabled = classCacheEnabled;
}
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
// 创建、缓存并返回,这是java8的特性
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
// 没有开启缓存,直接new
return new Reflector(type);
}
}
}
可见它的成员变量直接初始化了,其中reflectorMap就是用来存取Reflector。
3、变更数据
我们要更新builder,可以将它封装进来,构造注入保持前后一致。
private static String removeArrayMiddleField(String str, String field) {
StringBuilder builder = new StringBuilder();
AppendToken appendToken = new AppendToken("", builder);
String[] split = str.split("},");// 不一定有会报错吗split.length==1时
for (int i = 0; i < split.length; i++) {
if (split.length > 1 && i < split.length - 1) {
String s = removeMiddleField(split[i], field);
// builder.append(s).append("},");
builder.append(s);
appendToken.setToken(AppendTokenEnum.B.getToken()).appendToken();
} else {
String s = removeMiddleField(split[i], field);
builder.append(s);
}
}
return builder.toString();
}
/**
* 职责是追加占位符
*/
public class AppendToken {
private String token; // 占位符,因为它可以通过set更新故不能是final
private final StringBuilder builder; // 用于往builder追加占位符
/**
* 构造初始化
*
* @param token 占位符
* @param builder 往builder追加占位符,注意必须以之前的builder保持一致!!!
*/
public AppendToken(String token, StringBuilder builder) {
this.token = token;
this.builder = builder;
}
/**
* 更新占位符
*
* @param token 占位符
* @return appendToken以便调用appendToken
*/
public AppendToken setToken(String token) {
this.token = token;
return this;
}
/**
* 追加占位符
*/
public void appendToken() {
this.builder.append(token);
}
}
/**
* 占位符枚举类
*/
public enum AppendTokenEnum {
A("[{"),
B("},"),
C("}]"),
D(",");
private String token;
AppendTokenEnum(String token) {
this.token = token;
}
public String getToken() {
return token;
}
}
刚开始是没有把追加占位符封装起来的,直接builder.append(s).append("},");追加占位符。后面考虑了一下,因为占位符多种多样,于是封装了一个追加占位符的类AppendToken,那它的成员变量应该有占位符token、变更数据builder。占位符通过枚举类AppendTokenEnum封装维护易于扩展。
仔细研究一波又一波,走进源码世界就不再那么的稀里糊涂啦!!!
|