《java学习笔记》之多线程初步

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
System.out.println(myThread3);//Thread[Thread-0,5,main]

//启动新线程

//myThread3.start();//Thread-0

//修改线程的名字

myThread3.setName(“Thread1”);

//启动线程

myThread3.start();

//main线程

for (int i = 0; i < 100; i++) {

System.out.println(Thread.currentThread().getName() + " —> " + i);

}

}

}

class MyThread3 extends Thread{

public void run(){

//获取当前线程的名字

System.out.println(currentThread().getName());

for (int i = 0; i < 100; i++) {

System.out.println(currentThread().getName() + " —> " + i);

}

}

}

(二)

/*

  • 关于线程的sleep()方法

  • 1.静态方法

  • 2.参数是毫秒

  • 3.作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用

  •   这行代码出现在A线程中,A线程就会进入休眠。
    
  •   这行代码出现在B线程中,B线程就会进入休眠。
    
  • 4.Thread.sleep()方法,可以做到这样的效果:

  •   间隔特定的时间,去执行一段特定的代码,每隔多久执行一次
    
  • */

public class ThreadTest05 {

public static void main(String[] args) {

/*//在main进程中,所以使用sleep方法,是使得main方法进入阻塞状态

try {

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

//五秒后执行此代码

System.out.println(“Hello World”);

*/

//使得每个一秒钟输出一个数字

for (int i = 0; i < 10; i++) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(i);

}

}

}

/*

  • Thread.sleep()方法的面试题

  • */

public class ThreadTest06 {

public static void main(String[] args) {

//创建线程对象

MyThread5 myThread = new MyThread5();

myThread.setName(“ttt”);

myThread.start();

//请问这会使得 myThread 休眠一秒吗

try {

myThread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//不会,因为sleep是一个静态方法,最终在编译器会看做Thread.sleep() 这个的作用是休眠当前线程

//当前线程是main线程

System.out.println(“Hello World”);

//Hello World

//0

//1

//2

//3

//4

}

}

class MyThread5 extends Thread{

@Override

public void run() {

for (int i = 0; i < 5; i++) {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(i);

}

}

}

(三)

/*

  • 怎么唤醒正在休眠的线程?

  •   不是终断线程的执行,是终止线程的睡眠
    
  • */

public class ThreadTest07 {

public static void main(String[] args) {

//创建线程对象

Thread myThread6 =new Thread(new MyThread6());

myThread6.setName(“钢铁侠 Thread”);

myThread6.start();

//希望过5秒能让 myThread6 醒过来

//一年太长了

try {

//当前线程是主线程

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

//这个方法是利用了异常处理

终止t线程的睡眠

myThread6.interrupt();

}

}

class MyThread6 implements Runnable{

@Override

public void run() {

//休眠一年

System.out.println(Thread.currentThread().getName() + " —> begin");

try {

Thread.sleep(365 * 24 * 60 * 60 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//一年之后再执行

System.out.println(Thread.currentThread().getName() + “—> over”);

}

}

(四)

//在java中怎么强行终止一个线程的执行

public class ThreadTest08 {

public static void main(String[] args) {

MyThread7 myThread7 =new MyThread7();

myThread7.setName(“美国队长 Thread”);

myThread7.start();

//过五秒后让 myThread7 终止

try {

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

myThread7.stop();//已过时,不建议使用

}

//stop存在缺点,容易丢失数据,因为直接将线程杀死了

//美国队长 Thread --> 0

//美国队长 Thread --> 1

//美国队长 Thread --> 2

//美国队长 Thread --> 3

//美国队长 Thread --> 4

}

class MyThread7 extends Thread{

public void run(){

for (int i = 0; i < 100; i++) {

//每个一秒输出一个数字

System.out.println(Thread.currentThread().getName() + " --> " + i);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

(五)

/*

  • 怎么合理的终止一个线程的执行?

  • */

public class ThreadTest09 {

public static void main(String[] args) {

MyThread8 myThread =new MyThread8();

Thread myThread8 = new Thread(myThread);

myThread8.setName(“黑豹”);

myThread8.start();

//模拟五秒

try {

Thread.sleep(1000 * 5);

} catch (InterruptedException e) {

e.printStackTrace();

}

//想什么时候关闭,什么时候讲run 变成false就行了

myThread.run = false;

}

}

class MyThread8 implements Runnable{

//布尔标记

boolean run = true;

@Override

public void run() {

for (int i = 0; i < 100; i++) {

if (run){

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(i);

}else{

//有什么要保存的在这保存

return;

}

}

}

}

(六)

//了解:关于线程的优先级

public class ThreadTest010 {

public static void main(String[] args) {

//线程的优先级

System.out.println(Thread.MAX_PRIORITY);//10

System.out.println(Thread.NORM_PRIORITY);//5

System.out.println(Thread.MIN_PRIORITY);//1

//将main线程的优先级变为1

Thread.currentThread().setPriority(1);

Thread myThread9 = new Thread(new MyThread9());

myThread9.setName(“蜘蛛侠”);

优先级较高的,只是抢到的CPU时间片相对多一些

myThread9.start();

for (int i = 0; i < 1000; i++) {

System.out.println(“main —>” + i);

}

}

}

class MyThread9 implements Runnable{

@Override

public void run() {

System.out.println("线程的默认优先级是 " + Thread.currentThread().getPriority());

for (int i = 0; i < 1000; i++) {

System.out.println(Thread.currentThread().getName() + “—>” + i);

}

}

}

(七)

/*

  • 让位:

*当前线程暂停,回到就绪转态,让给其它线程

  • static void yield() 静态方法*/

public class ThreadTest11 {

public static void main(String[] args) {

Thread myThread = new MyThread10();

myThread.setName(“雷神”);

myThread.start();

for (int i = 0; i < 1000; i++) {

System.out.println("main —> " +i);

}

}

}

class MyThread10 extends Thread{

public void run(){

for (int i = 0; i < 1000; i++) {

//每输出100就让位一次

if (i %100 == 0){

Thread.yield();//当前线程暂停一下,让给主线程

}

System.out.println(Thread.currentThread().getName() + "—> " + i);

}

}

}

(八)

/*

  • 线程的合并

  • */

public class ThreadTest12 {

public static void main(String[] args) {

System.out.println(“main begin!!!”);

Thread myThread11 = new MyThread11();

myThread11.setName(“黑寡妇”);

myThread11.start();

try {

//不是栈的合并,是栈之间的协调

myThread11.join();//myThread11 合并到 主线程,主线程进入阻塞状态,当 myThread11 执行完再执行

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“main over”);

}

//main begin!!!

//0

//1

//2

//3

//4

//main over

}

class MyThread11 extends Thread{

@Override

public void run() {

for (int i = 0; i <5 ; i++) {

System.out.println(i);

}

}

}

五.线程安全


(一)模拟银行操作系统

public class Test {

public static void main(String[] args) {

//创建银行账户

Account account =new Account(“美国队长的账户”,10000);

//创建两个线程

//猎鹰和美国队长同时去取钱

AccountThread accountThread1 = new AccountThread(account);

AccountThread accountThread2 = new AccountThread(account);

accountThread1.setName(“美国队长”);

accountThread2.setName(“猎鹰”);

//取款

accountThread1.start();

accountThread2.start();

//猎鹰 对 美国队长的账户 取款了5000.0元,余额是 5000.0

//美国队长 对 美国队长的账户 取款了5000.0元,余额是 5000.0

//这就出错了

}

}

public class AccountThread extends Thread{

//同一个进程有相同的账号对象

//模拟两个人同时去ATM机器取钱

//has a

private Account account;

//构造方法,传入银行账户属性

public AccountThread(Account account) {

this.account = account;

}

@Override

//run方法的执行表示取款操作

public void run() {

//假设就取5000元钱

double money = 5000;

account.withdraw(money);

//取款信息

System.out.println(Thread.currentThread().getName() + " 对 " + account.toString() + " 取款了" + money + "元,余额是 " + account.getBalance());

}

}

//银行账户类

public class Account {

//账号

private String acton;

//余额

private double balance;

//构造方法

public Account() {

}

public Account(String acton, double balance) {

this.acton = acton;

this.balance = balance;

}

//set和get方法

public String getActon() {

return acton;

}

public void setActon(String acton) {

this.acton = acton;

}

public double getBalance() {

return balance;

}

public void setBalance(double balance) {

this.balance = balance;

}

//取款方法

public void withdraw(double money){

//取前余额

double before = this.balance;

//取后余额

double after = before - money;

//模拟网络延迟,就一定会出错

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//更新余额

setBalance(after);

}

@Override

public String toString() {

return acton;

}

}

(二)synchronized第一种使用方式

public class Test {

public static void main(String[] args) {

//创建银行账户

Account account =new Account(“美国队长的账户”,10000);

//创建两个线程

//猎鹰和美国队长同时去取钱

AccountThread accountThread1 = new AccountThread(account);

AccountThread accountThread2 = new AccountThread(account);

accountThread1.setName(“美国队长”);

accountThread2.setName(“猎鹰”);

//取款

accountThread1.start();

accountThread2.start();

//猎鹰 对 美国队长的账户 取款了5000.0元,余额是 5000.0

//美国队长 对 美国队长的账户 取款了5000.0元,余额是 5000.0

//这就出错了

}

}

public class AccountThread extends Thread{

//同一个进程有相同的账号对象

//模拟两个人同时去ATM机器取钱

//has a

private Account account;

//构造方法,传入银行账户属性

public AccountThread(Account account) {

this.account = account;

}

@Override

//run方法的执行表示取款操作

public void run() {

//假设就取5000元钱

double money = 5000;

account.withdraw(money);

//取款信息

System.out.println(Thread.currentThread().getName() + " 对 " + account.toString() + " 取款了" + money + "元,余额是 " + account.getBalance());

}

}

(三)synchronized第二种使用方式

//银行账户类

public class Account {

//账号

private String acton;

//余额

private double balance;

//构造方法

public Account() {

}

public Account(String acton, double balance) {

this.acton = acton;

this.balance = balance;

}

//set和get方法

public String getActon() {

return acton;

}

public void setActon(String acton) {

this.acton = acton;

}

public double getBalance() {

return balance;

}

public void setBalance(double balance) {

this.balance = balance;

}

//取款方法

public void withdraw(double money){

//以下的代码必须是排队的,要不然就可能会出问题

//一个线程把这里的代码全部执行完之后,另一个线程才能进来

/*

  • 线程同步代码的语法

  • synchronized(){

}

  • synchronized 是一个关键字

  • 后面的小括号填的东西非常重要,填的是你想要同步的线程的数据

  • 这个数据必须是多线程的共享数据,才能达到多线程的排队

  • ()里面写什么?

  •   那看你想让哪些线程同步?
    
  •   假设t1,t2,t3,t4,t5,有五个线程
    
  •   你只希望t1,t2,t3排队,t4,t5不需要排队,怎么办?
    
  •   你就在()中写一个t1,t3,t3共享的对象,而这个对象对于t4 t5来说是不共享的
    
  • */

/*

  • 对于这里来说,银行的账户是共享的,就可以填银行的账户

  • withdraw 是一个实例方法,所以this 就是银行对象

  • 在java中,每一个对象都有一个对象锁

  • 多少个对象,多少把锁

  • 以下是线程同步的原理:

  • t1和t2两个线程:

  •   1. 假设t1和t2线程是并发的,那么肯定一先一后执行下面的代码
    
  •       假设t1先执行到这个代码,t1看到 synchronized 关键字就会去找后面的“后面共享对象”的对象锁,
    
  •       找到之后就会占有这把锁,然后执行同步代码块的代码,在程序中一直占有这把锁,直到t1执行完毕,才会释放这个
    
  •       “共享对象”的对象锁
    
  •   2.假设t1已经占有了这把锁,t2这时候进到了synchronized中,去寻找对象锁,找到了对象锁,但是发现对象锁被t1占有了
    
  •       那么也只能停下来,等到t1之星完毕,释放对象锁,才可以执行同步代码块
    
  • 这样就达到了线程排队执行

  •   这里要注意的是:这个共享对象一定要选好,
    
  •   这个共享对象 是需要排队执行的这些线程对象所共享的
    
  • */

//synchronized (object){ //也可以

//synchronized (“abc”){ //"abc"在字符串常量池当中,所有的线程都会等待

synchronized (this){

//取前余额

double before = this.balance;

//取后余额

double after = before - money;

//即使有网络延迟也没关系了

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//更新余额

setBalance(after);

}

}

@Override

public String toString() {

return acton;

}

}

package caopeng.javaseTest.threadsafetest.treadsafe3;

//银行账户类

public class Account {

//账号

private String acton;

//余额

private double balance;

//构造方法

public Account() {

}

public Account(String acton, double balance) {

this.acton = acton;

this.balance = balance;

}

//set和get方法

public String getActon() {

return acton;

}

public void setActon(String acton) {

this.acton = acton;

}

public double getBalance() {

return balance;

}

public void setBalance(double balance) {

this.balance = balance;

}

//取款方法

//在方法的声明上使用synchronized,那么锁住一定是 this

//方法体就是同步代码块,可能会扩大同步的范围

//如果共享的就是this,并且需要同步的就是整个方法体,建议使用这个

//缺点:不够灵活

//优点:代码整洁

public synchronized void withdraw(double money){

//取前余额

double before = this.balance;

//取后余额

double after = before - money;

//模拟网络延迟,就一定会出错

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

//更新余额

setBalance(after);

}

@Override

public String toString() {

return acton;

}

}

(四)synchronized第三种使用方式

在静态方法上用 synchronized 修饰,这个时候占用的是类锁,一个类一个锁,这个类的所有对象占有这一把锁

在下面的题目中最后一题有具体的代码实现

六.简单的Synchronized题目


(一)题目一

//问 doOther需要等 dosome 结束吗

public class Exam {

public static void main(String[] args) {

//创建一个myClass对象

MyClass myClass =new MyClass();

//创建两个线程对象

Thread thread1 = new MyThread(myClass);

Thread thread2 = new MyThread(myClass);

thread1.setName(“钢铁侠”);

thread2.setName(“美国队长”);

//启动线程

thread1.start();

try {

Thread.sleep(1000);//确保 钢铁侠 线程先执行

} catch (InterruptedException e) {

e.printStackTrace();

}

thread2.start();

}

//dosome begin!!!

//doOther begin!!!

//doOther over!!!

//dosome over!!!

//不需要,因为doOther没有synchronized修饰,不需要对象锁,即使 myclass 对象的对象锁的确被 钢铁侠线程占有了

}

class MyThread extends Thread{

MyClass myClass;

public MyThread(MyClass myClass){

this.myClass = myClass;

}

public void run(){

if(Thread.currentThread().getName().equals(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

(二)题目二

//问 doOther需要等 dosome 结束吗

public class Exam {

public static void main(String[] args) {

//创建一个myClass对象

MyClass myClass =new MyClass();

//创建两个线程对象

Thread thread1 = new MyThread(myClass);

Thread thread2 = new MyThread(myClass);

thread1.setName(“钢铁侠”);

thread2.setName(“美国队长”);

//启动线程

thread1.start();

try {

Thread.sleep(1000);//确保 钢铁侠 线程先执行

} catch (InterruptedException e) {

e.printStackTrace();

}

thread2.start();

}

//dosome begin!!!

//dosome over!!!

//doOther begin!!!

//doOther over!!!

//需要,因为 doOther此时也有Synchronized修饰,需要对象锁,但是doSome方法先执行,抢占了对象锁

//所以只能等 dosome方法执行完之后才能占有对象锁,然后执行同步代码块

}

class MyThread extends Thread{

MyClass myClass;

public MyThread(MyClass myClass){

this.myClass = myClass;

}

public void run(){

if(Thread.currentThread().getName().equals(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public synchronized void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

(三)题目三

//问 doOther需要等 dosome 结束吗

public class Exam {

public static void main(String[] args) {

//创建两个myClass对象

MyClass myClass1 =new MyClass();

MyClass myClass2 =new MyClass();

//创建两个线程对象

Thread thread1 = new MyThread(myClass1);

Thread thread2 = new MyThread(myClass2);

thread1.setName(“钢铁侠”);

thread2.setName(“美国队长”);

//启动线程

thread1.start();

try {

Thread.sleep(1000);//确保 钢铁侠 线程先执行

} catch (InterruptedException e) {

e.printStackTrace();

}

thread2.start();

}

//dosome begin!!!

//doOther begin!!!

//doOther over!!!

//dosome over!!!

//不需要,因为现在有两个对象,对象锁,一个对象一把锁,多少个对象,多少把锁

//现在有两个对象, 钢铁侠 线程占有的是myClass1的对象锁

//与myClass2 的对象锁 没有关系,所以不会影响到 doOther方法的执行

}

class MyThread extends Thread{

MyClass myClass;

public MyThread(MyClass myClass){

this.myClass = myClass;

}

public void run(){

if(Thread.currentThread().getName().equals(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public synchronized void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

(四)题目四

//问 doOther需要等 dosome 结束吗

public class Exam {

public static void main(String[] args) {

//创建两个myClass对象

MyClass myClass1 =new MyClass();

MyClass myClass2 =new MyClass();

//创建两个线程对象

Thread thread1 = new MyThread(myClass1);

Thread thread2 = new MyThread(myClass2);

thread1.setName(“钢铁侠”);

thread2.setName(“美国队长”);

//启动线程

thread1.start();

try {

Thread.sleep(1000);//确保 钢铁侠 线程先执行

} catch (InterruptedException e) {

e.printStackTrace();

}

thread2.start();

}

//dosome begin!!!

//dosome over!!!

//doOther begin!!!

//doOther over!!!

//需要,因为现在 Synchronized 修饰的是 静态方法,静态方法占有的是 类锁

//一个类一把锁,不管创建了几个对象,都是占用这一把锁, 钢铁侠 线程先执行,占用了MyClass的类锁

//美队执行到doOther的时候,发现没有这把锁,就要停下来等 钢铁侠 线程执行完毕

}

class MyThread extends Thread{

MyClass myClass;

public MyThread(MyClass myClass){

this.myClass = myClass;

}

public void run(){

if(Thread.currentThread().getName().equals(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized static void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public synchronized static void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

七.死锁


//死锁代码要会写,只有会写,才可能会调试

//死锁很难调试,执行不会结束,又不报异常

public class DeadLock {

public static void main(String[] args) {

Object object1 = new Object();

Object object2 = new Object();

//创建两个线程

Thread thread1 = new Thread1(object1,object2);

Thread thread2 = new Thread(new Thread2(object1,object2));

//启动线程

thread1.start();

thread2.start();

}

}

class Thread1 extends Thread{

Object object1;

Object object2;

public Thread1() {

}

public Thread1(Object object1, Object object2) {

this.object1 = object1;

this.object2 = object2;

}

public void run(){

synchronized (object1){

try {

Thread.sleep(1000 );

} catch (InterruptedException e) {

e.printStackTrace();

}

//嵌套 ,锁完 object1再锁object2

synchronized (object2){

}

}

}

}

class Thread2 implements Runnable{

Object object1;

Object object2;

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!
MyClass myClass;

public MyThread(MyClass myClass){

this.myClass = myClass;

}

public void run(){

if(Thread.currentThread().getName().equals(“钢铁侠”)){

myClass.dosome();

}else if (Thread.currentThread().getName().equals(“美国队长”)){

myClass.doOther();

}

}

}

class MyClass{

public synchronized static void dosome(){

System.out.println(“dosome begin!!!”);

try {

Thread.sleep(10 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(“dosome over!!!”);

}

public synchronized static void doOther(){

System.out.println(“doOther begin!!!”);

System.out.println(“doOther over!!!”);

}

}

七.死锁


//死锁代码要会写,只有会写,才可能会调试

//死锁很难调试,执行不会结束,又不报异常

public class DeadLock {

public static void main(String[] args) {

Object object1 = new Object();

Object object2 = new Object();

//创建两个线程

Thread thread1 = new Thread1(object1,object2);

Thread thread2 = new Thread(new Thread2(object1,object2));

//启动线程

thread1.start();

thread2.start();

}

}

class Thread1 extends Thread{

Object object1;

Object object2;

public Thread1() {

}

public Thread1(Object object1, Object object2) {

this.object1 = object1;

this.object2 = object2;

}

public void run(){

synchronized (object1){

try {

Thread.sleep(1000 );

} catch (InterruptedException e) {

e.printStackTrace();

}

//嵌套 ,锁完 object1再锁object2

synchronized (object2){

}

}

}

}

class Thread2 implements Runnable{

Object object1;

Object object2;

最后

面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?

掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。

[外链图片转存中…(img-z5a5gTuz-1714773748171)]

[外链图片转存中…(img-mUdMpiYG-1714773748172)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门,即可获取!

  • 14
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值