学习碰到的问题
@JsonFormat和@DataFormat注解解决前后端日期格式一致性问题
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date blog_date;
startsWith
判断此字符串的子字符串是否从指定的索引开始,并以指定的前缀开头
// 判断此字符串的子字符串是否从指定的索引开始,并以指定的前缀开头
public boolean startsWith(String prefix, int toffset) {
char ta[] = value;
int to = toffset;
char pa[] = prefix.value;
int po = 0;
int pc = prefix.value.length;
// Note: toffset might be near -1>>>1.
// 如果起始地址小于0或者(起始地址+所比较对象长度)大于自身对象长度,返回false
if ((toffset < 0) || (toffset > value.length - pc)) {
return false;
}
// 从所比较对象的末尾开始比较
while (--pc >= 0) {
if (ta[to++] != pa[po++]) {
return false;
}
}
return true;
}
// startsWith重载方法1
public boolean startsWith(String prefix) {
return startsWith(prefix, 0);
}
// startsWith重载方法2
public boolean endsWith(String suffix) {
return startsWith(suffix, value.length - suffix.value.length);
}
concat
// 将指定的字符串连接到该字符串的末尾
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
// 注意:这里是新new一个String对象返回,而并非原来的String对象
return new String(buf, true);
}
// 位于 java.util.Arrays 类中
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
// 调用底层c++
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
replace
// 返回一个字符串,该字符串是通过用 newChar 替换该字符串中所有出现的 oldChar 而产生的
public String replace(char oldChar, char newChar) {
// 新旧值先对比
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
// 找到旧值最开始出现的位置
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
// 从那个位置开始,直到末尾,用新值代替出现的旧值
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
// 注意:这里是新new一个String对象返回,而并非原来的String对象
return new String(buf, true);
}
}
return this;
}
trim
返回值是此字符串的字符串,其中已删除所有前导和尾随的空格
StringBuilder 源码分析
首先 StringBuilder 同 String 一样 被 final 关键字修饰该类不能被继承!
从继承体系可以看出, StringBuilder 继承了AbstractStringBuilder类,该类中包含了可变字符串的相关操作方法:append()、insert()、delete()、replace()、charAt()等等。**StringBuffer 和 StringBuilder ** 均继承该类!
比如在 StringBuilder 中的 append(String str) 方法(后文还会讲到):
// java.lang.StringBuilder 类中
@Override
public StringBuilder append(String str) {
// 调用父类 AbstractStringBuilder 的append(String str) 方法
super.append(str);
return this;
}
// java.lang.AbstractStringBuilder 类中(有兴趣可以自己点进去阅读该类的源码,还是很有收获的,这里只作简述!)
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
// 其成员属性value[]数组扩容,类似于ArrayList 的扩容
// 扩容算法:int newCapacity = (value.length << 1) + 2;
// 注意容量有上限:MAX_ARRAY_SIZE
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
StringBuilder 还实现了Serializable 接口 和 CharSequence 接口,说明该类的对象可以被序列化,并附带了对字符序列进行只读访问的方法,比如:length()、charAt()、subSequence()、toString()方法等。
/**
* 用于字符存储的char数组,value是一个动态的数组,当存储容量不足时,会对它进行扩容.
*/
char[] value;
/**
* 表示value数组中已存储的字符数.
*/
int count;
关键点
-
该数组和 Sting 中的value数组不同,其未被 final 关键字修饰,其值是可以被修改的!
-
这两个成员属性并不位于java.lang.StringBuilder 中,而位于其父类 java.lang.AbstractStringBuilder 中,同样的StringBuffer 中的这两个属性也是位于该父类中!
StringBuilder 类中的构造方法
StringBuilder类提供了4个构造方法。构造方法主要完成了对value数组的初始化
// 位于父类StringBuilder 中
// 空参StringBuilder 默认char数组容量为16
public StringBuilder() {
super(16);
}
// 由参数传入容量,构建StingBuilder
public StringBuilder(int capacity) {
super(capacity);
}
// 根据字符串参数的长度,构建容量为 16 + 字符串长度的StringBuilder
public StringBuilder(String str) {
super(str.length() + 16);
// 调用父类append 方法添加字符串str
append(str);
}
// 接收一个CharSequence对象作为参数,设置了value数组的初始容量为CharSequence对象的长度+16
// 并把CharSequence对象中的字符添加到value数组中
public StringBuilder(CharSequence seq) {
this(seq.length() + 16);
// 调用父类append 方法添加字符串seq
append(seq);
}
// 位于父类AbstractStringBuilder中
// 创建指定容量的AbstractStringBuilder
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
总结
其实String 、StingBuilder 、StirngBuffer 也类似于容器,因为其底层均在维护一个 char 类型的数组!
append()方法将指定参数类型的字符串表示形式追加到字符序列的末尾。
StringBuilder 类提供了一系列的append()方法,它可以接受boolean、char、char[]、CharSequence、double、float、int、long、Object、String、StringBuffer这些类型的参数。
这些方法最终都调用了父类AbstractStringBuilder类中对应的方法。最后,append()方法返回了StringBuilder对象自身,以便用户可以链式调用StringBuilder类中的方法。
AbstractStringBuilder类的各个append()方法大同小异。append()方法在追加字符到value数组中之前都会调用ensureCapacityInternal()方法来确保value数组有足够的容量,然后才把字符追加到value数组中。
StringBuffer 的扩容
// 位于StringBuffer 中
@Override
public synchronized void ensureCapacity(int minimumCapacity) {
super.ensureCapacity(minimumCapacity);
}
// 位于父类中
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0)
ensureCapacityInternal(minimumCapacity);
}
StringBuffer对其进行了重写,直接调用父类的expandCapacity方法。这个方法用来保证value的长度大于给定的参数minimumCapacity,在父类的ensureCapacityInternal方法中这样操作:
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}
最后得到的新value数组大小是max(value.length*2+2,minimumCapacity),上面代码中的第二个判断是为了防止newCapacity溢出。
setLength方法
该方法用于直接设置字符数组元素的count数量:
// 位于StringBuffer 中
@Override
public synchronized void setLength(int newLength) {
toStringCache = null;
super.setLength(newLength);
}
// 为于父类中
public void setLength(int newLength) {
if (newLength < 0)
throw new StringIndexOutOfBoundsException(newLength)
ensureCapacityInternal(newLength);
if (count < newLength) {
Arrays.fill(value, count, newLength, '\0');
}
count = newLength;
}
从代码中可以看出,如果newLength大于count,那么就会在后面添加’\0’补充;如果小于count,就直接使count = newLength。
appen/insert 方法
StringBuffer中的每一个append和insert函数都会调用父类的函数:
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
@Override
public synchronized StringBuffer insert(int index, char[] str, int offset, int len) {
toStringCache = null;
super.insert(index, str, offset, len);
return this;
}
ensureCapacityInternal()方法判断value数组的容量是否足够,如果不够,那么调用newCapacity()方法进行扩容。
newCapacity()方法默认情况下将数组容量扩大到原数组容量的2倍+2。数组的容量最大只能扩容到Integer.MAX_VALUE。
最后,调用Arrays类的copyOf()静态方法来创建一个新数组和拷贝原数据到新数组,并将value指向新数组。
delete()方法
delete()方法删除指定位置的字符。删除的字符从指定的start位置开始,直到end-1位置。delete()方法也调用了父类AbstractStringBuilder类中对应的方法。
delete()方法首先检查参数的合法性。当end大于value数组中已存储的字符数count时,end取count值。最后,当需要删除的字符数大于1的时候,调用System类的arraycopy()静态方法进行数组拷贝完成删除字符的操作,并更新count的值。
replace()方法
// 位于父类StringBuilder 中
@Override
public StringBuilder replace(int start, int end, String str) {
super.replace(start, end, str);
return this;
}
// 位于父类AbstractStringBuilder中
public AbstractStringBuilder replace(int start, int end, String str) {
if (start < 0)
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count;
int len = str.length();
int newCount = count + len - (end - start);
ensureCapacityInternal(newCount);
System.arraycopy(value, end, value, start + len, count - end);
str.getChars(value, start);
count = newCount;
return this;
}
// 位于java.lang.String 中
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
replace()方法将指定位置的字符替换成指定字符串中的字符。替换的字符从指定的start位置开始,直到end-1位置。
replace()方法也调用了父类AbstractStringBuilder类中对应的方法。
replace()方法首先检查参数的合法性。当end大于value数组中已存储的字符数count时,end取count值。然后调用ensureCapacityInternal()方法确保value数组有足够的容量。接着调用System类的arraycopy()静态方法进行数组拷贝,主要的作用是从start位置开始空出替换的字符串长度len大小的位置。最后,调用String类的getChars()方法将替换的字符串中的字符拷贝到value数组中。这样就完成了替换字符的操作。
toString()方法
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
toString()方法返回一个表示该字符序列的字符串,将SrpingBuilder 转换成了 String 类型
总结
-
StringBuilder类使用了一个char数组来存储字符。该数组是一个动态的数组,当存储容量不足时,会对它进行扩容。
-
StringBuilder对象是一个可变的字符序列。
-
StringBuilder类是非线程安全的。
-
StringBuffer 和 StringBuilder 在添加
append()方法上的区别是,前者加上了synchronized 锁,因此其是线程安全的,后者是非线程安全的!而判断是否扩容以及扩容方式,StringBuffer 和 StringBuilder 二者没什么区别!
@PostConstruct注解介绍 顺序
在Spring框架中,如果某个方法需要使用@PostConstruct注解修饰的话,那么加上此注解的该方法在整个Bean 初始中的执行顺序为:-
Constructor(构造方法) —>@Autowired(依赖注入) —>@PostConstruct(注释的方法)
码云git常用命令总结
仓库
### 在当前目录新建一个Git代码库
$ git init
### 新建一个目录,将其初始化为Git代码库
$ git init [project-name]
### 下载一个项目和它的整个代码历史
$ git clone [url]
配置
### 显示当前的Git配置
$ git config --list
### 编辑Git配置文件
$ git config -e [--global]
### 设置提交代码时的用户信息
$ git config [--global] user.name "[name]"
$ git config [--global] user.email "[email address]"
增加/删除文件
### 添加指定文件到暂存区
$ git add [file1] [file2] ...
### 添加指定目录到暂存区,包括子目录
$ git add [dir]
### 添加当前目录的所有文件到暂存区
$ git add .
### 添加每个变化前,都会要求确认
### 对于同一个文件的多处变化,可以实现分次提交
$ git add -p
### 删除工作区文件,并且将这次删除放入暂存区
$ git rm [file1] [file2] ...
### 停止追踪指定文件,但该文件会保留在工作区
$ git rm --cached [file]
### 改名文件,并且将这个改名放入暂存区
$ git mv [file-original] [file-renamed]
代码提交
### 提交暂存区到仓库区
$ git commit -m [message]
### 提交暂存区的指定文件到仓库区
$ git commit [file1] [file2] ... -m [message]
### 提交工作区自上次commit之后的变化,直接到仓库区
$ git commit -a
### 提交时显示所有diff信息
$ git commit -v
### 使用一次新的commit,替代上一次提交
### 如果代码没有任何新变化,则用来改写上一次commit的提交信息
$ git commit --amend -m [message]
### 重做上一次commit,并包括指定文件的新变化
$ git commit --amend [file1] [file2] ...
分支
列出所有本地分支
$ git branch
列出所有远程分支
$ git branch -r
列出所有本地分支和远程分支
$ git branch -a
新建一个分支,但依然停留在当前分支
$ git branch [branch-name]
新建一个分支,并切换到该分支
$ git checkout -b [branch]
新建一个分支,指向指定commit
$ git branch [branch] [commit]
新建一个分支,与指定的远程分支建立追踪关系
$ git branch --track [branch] [remote-branch]
切换到指定分支,并更新工作区
$ git checkout [branch-name]
切换到上一个分支
$ git checkout -
建立追踪关系,在现有分支与指定的远程分支之间
$ git branch --set-upstream [branch] [remote-branch]
合并指定分支到当前分支
$ git merge [branch]
选择一个commit,合并进当前分支
$ git cherry-pick [commit]
删除分支
$ git branch -d [branch-name]
删除远程分支
$ git push origin --delete [branch-name]
$ git branch -dr [remote/branch]
标签
列出所有tag
$ git tag
新建一个tag在当前commit
$ git tag [tag]
新建一个tag在指定commit
$ git tag [tag] [commit]
删除本地tag
$ git tag -d [tag]
删除远程tag
$ git push origin :refs/tags/[tagName]
查看tag信息
$ git show [tag]
提交指定tag
$ git push [remote] [tag]
提交所有tag
$ git push [remote] --tags
新建一个分支,指向某个tag
$ git checkout -b [branch] [tag]
查看信息
显示有变更的文件
$ git status
显示当前分支的版本历史
$ git log
显示commit历史,以及每次commit发生变更的文件
$ git log --stat
搜索提交历史,根据关键词
$ git log -S [keyword]
显示某个commit之后的所有变动,每个commit占据一行
$ git log [tag] HEAD --pretty=format:%s
显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件
$ git log [tag] HEAD --grep feature
显示某个文件的版本历史,包括文件改名
$ git log --follow [file]
$ git whatchanged [file]
显示指定文件相关的每一次diff
$ git log -p [file]
显示过去5次提交
$ git log -5 --pretty --oneline
显示所有提交过的用户,按提交次数排序
$ git shortlog -sn
显示指定文件是什么人在什么时间修改过
$ git blame [file]
显示暂存区和工作区的差异
$ git diff
显示暂存区和上一个commit的差异
$ git diff --cached [file]
显示工作区与当前分支最新commit之间的差异
$ git diff HEAD
# 显示两次提交之间的差异
$ git diff [first-branch]...[second-branch]
显示今天你写了多少行代码
$ git diff --shortstat "@{0 day ago}"
显示某次提交的元数据和内容变化
$ git show [commit]
显示某次提交发生变化的文件
$ git show --name-only [commit]
显示某次提交时,某个文件的内容
$ git show [commit]:[filename]
显示当前分支的最近几次提交
$ git reflog
远程同步
下载远程仓库的所有变动
$ git fetch [remote]
显示所有远程仓库
$ git remote -v
显示某个远程仓库的信息
$ git remote show [remote]
增加一个新的远程仓库,并命名
$ git remote add [shortname] [url]
取回远程仓库的变化,并与本地分支合并
$ git pull [remote] [branch]
上传本地指定分支到远程仓库
$ git push [remote] [branch]
强行推送当前分支到远程仓库,即使有冲突
$ git push [remote] --force
推送所有分支到远程仓库
$ git push [remote] --all
撤销
恢复暂存区的指定文件到工作区
$ git checkout [file]
恢复某个commit的指定文件到暂存区和工作区
$ git checkout [commit] [file]
恢复暂存区的所有文件到工作区
$ git checkout .
重置暂存区的指定文件,与上一次commit保持一致,但工作区不变
$ git reset [file]
重置暂存区与工作区,与上一次commit保持一致
$ git reset --hard
重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变
$ git reset [commit]
重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致
$ git reset --hard [commit]
重置当前HEAD为指定commit,但保持暂存区和工作区不变
$ git reset --keep [commit]
新建一个commit,用来撤销指定commit
后者的所有变化都将被前者抵消,并且应用到当前分支
$ git revert [commit]
暂时将未提交的变化移除,稍后再移入
$ git stash
$ git stash pop
其他
# 生成一个可供发布的压缩包
$ git archive
博客围绕Java学习展开,介绍了@JsonFormat和@DataFormat注解解决前后端日期格式一致性问题,对StringBuilder进行源码分析,探讨其与StringBuffer的区别及扩容机制,还提及@PostConstruct注解执行顺序,最后总结了码云git常用命令,包括仓库配置、文件操作、代码提交等。
465

被折叠的 条评论
为什么被折叠?



