public String replace(CharSequence target, CharSequence replacement) {
return Pattern.compile(target.toString(), Pattern.LITERAL).matcher(
this).replaceAll(Matcher.quoteReplacement(replacement.toString()));
}
replaceAll()
public String replaceAll(String regex, String replacement) {
return Pattern.compile(regex).matcher(this).replaceAll(replacement);
}
首先分析replaceAll()执行的流程
- (1)Pattern.compile(regex)生成一个正则表达式的编译表示Pattern
- (2)Pattern对象调用matcher(this)得到一个Matcher对象
- (3)Matcher对象replaceAll(replacement)返回一个字符串对象
- (1)生成正则表达式对象时
public static Pattern compile(String regex) {
return new Pattern(regex, 0);
}
private Pattern(String p, int f) {
pattern = p;
flags = f;
// to use UNICODE_CASE if UNICODE_CHARACTER_CLASS present
if ((flags & UNICODE_CHARACTER_CLASS) != 0)
flags |= UNICODE_CASE;
// Reset group index count
capturingGroupCount = 1;
localCount = 0;
if (pattern.length() > 0) {
//调用这个方法
compile();
} else {
root = new Start(lastAccept);
matchRoot = lastAccept;
}
}
private void compile() {
//此处省略n行生成树结构的核心代码
}
- (2)在上述生成一个Pattern的情况下(简单理解为一棵树),调用matcher()方法,将原字符串作为参数传递进去进行相应的匹配
public Matcher matcher(CharSequence input) {
if (!compiled) {
synchronized(this) {
if (!compiled)
compile();
}
}
Matcher m = new Matcher(this, input);
return m;
}
- (3)Matcher对象调用自身的replaceAll()方法
public String replaceAll(String replacement) {
reset();
boolean result = find();
if (result) {
StringBuffer sb = new StringBuffer();
do {
appendReplacement(sb, replacement);
result = find();
} while (result);
appendTail(sb);
return sb.toString();
}
return text.toString();
}
主要的时间复杂度在于生成一个树结构
replace()方法的区别在于一开始传入的就是一个字符串,不用生成一个正则表达式树形结构,其余的过程都是差不多的
(2)concat()
参数值的长度为0,直接返回原字符串,否则先创建一个新的字符数组buf(长度为原字符串的长度+拼接的字符串长度),调用参数值的getChars()方法将自身复制到buf中,最终返回一个新的字符串
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);
//重点,返回新字符串
return new String(buf, true);
}
重载的两个方法最终都调用的是System.arraycopy()方法
//两个参数,目标数组,目标数组的初始下标
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
//四个参数,源数组的开始、结尾,目标数组,目标数组的初始下标
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
(3)split(String regerx, int limit)
- limit=0,表示按照正则表达式分割无数次,最终省略最后的空字符串
- limit>0,分割n-1次数
- limit<0,分割无数次,不省略最后的空字符串
public final void join() throws InterruptedException {
//实际调用了join(int millis)
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
//一进入这个方法就设置一个基础的时间标
long base = System.currentTimeMillis();
//当前时间距base的时间间隔
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
// public final native boolean isAlive();
//调用了本地方法,判断当前线程是否存活
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
//delay<0说明已经超过了预设的时间
if (delay <= 0) {
break;
}
wait(delay);
//还需要求一次now,用作下一次循环的判断
now = System.currentTimeMillis() - base;
}
}
}
public final native void wait(long timeout) throws InterruptedException;
应该注意的是,如果要使得多个线程在某个主线程里面有顺序地执行(下面的线程数量太少,可以在线程里面加一个循环或是增加线程的数量,使得结果更明显)
- 可以在各自的run()方法里面使用join()方法
public static void main(String[] args) {
final Thread T1 = new Thread(new Runnable() {
public void run() {
System.out.println("T1 run");
for(int i=0;i<100;i++){
System.out.println("T1 is running" + i);
}
System.out.println("T1 run end");
}
});
final Thread T2 = new Thread(new Runnable() {
public void run() {
System.out.println("T2 run");
try{
T1.join();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("T2 run end");
}
});
Thread T3 = new Thread(new Runnable() {
public void run() {
System.out.println("T3 run");
try{
T2.join();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("T3 run end");
}
});
T1.start();
T2.start();
T3.start();
}
- 也可以在主线程里面使用start()和join()方法,但是需要先start()启动线程,再使用join()方法,因为由上述join()源码可知会先调用isAlive()判断线程是否存活,不存活的话不会执行wait()方法,3个线程还是处于竞争状态,不会顺序执行。
public static void main(String[] args) throws InterruptedException {
final Thread T1 = new Thread(new Runnable() {
public void run() {
System.out.println("T1 run");
for(int i=0;i<100;i++){
System.out.println("T1 is running" + i);
}
System.out.println("T1 run end");
}
});
final Thread T2 = new Thread(new Runnable() {
public void run() {
System.out.println("T2 run");
for(int i=0;i<100;i++){
System.out.println("T2 is running" + i);
}
try{
//T1.join();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("T2 run end");
}
});
Thread T3 = new Thread(new Runnable() {
public void run() {
System.out.println("T3 run");
for(int i=0;i<100;i++){
System.out.println("T3 is running" + i);
}
try{
//T2.join();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("T3 run end");
}
});
T1.start();
T1.join();
T2.start();
T2.join();
T3.start();
T3.join();
}
(2)使用进程间通信机制,基于wait()方法
private static boolean T2Run = false; //标识位,用来通知T2线程执行
private static boolean T3Run = false;
public static void main(String[] args) {
Object lock1 = new Object();
Object lock2 = new Object();
Thread T1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock1){
System.out.println("T1 run");
for(int i=0;i<100;i++){
System.out.println("T1 is running" + i);
}
//t1 线程通知t2执行
T2Run = true;
lock1.notify();
}
}
});
Thread T2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock1){
if(!T2Run){
System.out.println("T2 wait");
try {
lock1.wait();
} catch (Exception e){
e.printStackTrace();
}
}
System.out.println("T2 run");
for(int i=0;i<100;i++){
System.out.println("T2 is running" + i);
}
//t2 线程通知t3执行
synchronized (lock2){
T3Run = true;
lock2.notify();
}
}
}
});
Thread T3 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock2){
if (!T3Run){
System.out.println("T3 wait");
try {
lock2.wait();
} catch (Exception e){
e.printStackTrace();
}
}
System.out.println("T3 run");
for(int i=0;i<100;i++){
System.out.println("T3 is running" + i);
}
}
}
});
T1.start();
T2.start();
T3.start();
}
- 上述代码中最核心的变量是标志变量,两个对象锁变量,最核心的方法是notify(),wait(),注意这两个方法的调用者是锁对象,分别使得等待改对象的线程中的任意一个被唤醒、使得当前线程等待,直到被唤醒或者达到预设的时间,由源码注释可知
- 重点注意的是调用wait()方法的对象应该被同步
synchronize(someobject){
someobject.wait();
}
notify
wait
(3)使用线程池newSingleThreadExecutor,核心线程数和最大线程数都为1,但是不推荐使用,因为阻塞队列没有限制,容易导致内存溢出
static ExecutorService executorService = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
Thread T1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("T1 run");
}
});
Thread T2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("T2 run");
}
});
Thread T3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("T3 run");
}
});
executorService.submit(T1);
executorService.submit(T2);
executorService.submit(T3);
executorService.shutdown();
}
(4)使用CountDownLatch,相比于wait()的优点就是可以等待多个线程执行完毕之后再执行某个线程
- CountDownLatch(int count); //构造方法,创建一个值为count 的计数器
- await();//阻塞当前线程,将当前线程加入阻塞队列。
- countDown();//对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。
static CountDownLatch countDownLatch1 = new CountDownLatch(1);
static CountDownLatch countDownLatch2 = new CountDownLatch(1);
public static void main(String[] args) {
Thread T1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("T1 run");
countDownLatch1.countDown();
System.out.println("T1 countDown finish");
}
});
Thread T2 = new Thread(new Runnable() {
@Override
public void run() {
try{
countDownLatch1.await();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("T2 run");
countDownLatch2.countDown();
}
});
Thread T3 = new Thread(new Runnable() {
@Override
public void run() {
try{
countDownLatch2.await();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("T3 run");
}
});
}
(5)CyclicBarrier,多个线程达到某一个节点之后,再一起继续往下执行
- CyclicBarrier(int parties) //构造方法,参数表示拦截的线程的个数
- CyclicBarrier(int parties, Runnable barrierAction) //也是构造方法,可以通过后面的参数,这是线程的优先级
- await() //告诉CyclicBarrier自己已经到达同步点,然后当前线程被阻塞,当所有线程都到达同步点(barrier)时,唤醒所有的等待线程,一起往下继续运行,可根据参数barrierAction决定优先执行的线程
static CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
static CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);
public static void main(String[] args) {
Thread T1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("T1 run");
try{
cyclicBarrier1.await();
}catch (Exception e){
e.printStackTrace();
}
}
});
Thread T2 = new Thread(new Runnable() {
@Override
public void run() {
try{
cyclicBarrier1.await();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("T2 run");
try{
cyclicBarrier2.await();
}catch (Exception e){
e.printStackTrace();
}
}
});
Thread T3 = new Thread(new Runnable() {
@Override
public void run() {
try{
cyclicBarrier2.await();
}catch (Exception e){
e.printStackTrace();
}
System.out.println("T3 run");
}
});
T1.start();;
T2.start();
T3.start();
}
(5)使用condition 和 Semaphore信号量 略
6.Java中的基本IO
- 此处引用:https://cloud.tencent.com/developer/article/1785545
- 总结的较为精辟的就是:Reader,Writer;InputStream,OutputStream,后者可以处理所有的文件,但是因为是以字节为单位进行操作,遇到某些字符是超过1个字节的(汉字)就会出现乱码,所以出现了Reader,Writer。
- 字节流和字符流的转换使用转换流;提高读写的速度使用缓冲流或使用数组,因为涉及到用户态和核心态的切换,如果能增加一次读写的量,就会减少转换的次数
- 在写文件之后需要使用flush(),close(),前者将缓冲区中的数据写出(默认一个缓冲区的数据写满之后会自动进行写出,但是最后的数据没有达到缓冲区的大小的话就不会自动写,需要强制flush()),后者的话是减少资源的占用,所以需要进行手动关闭。



