多线程的继续理解和一些安全问题和设计模式


1.单例设计模式

* 单例设计模式:保证类在内存中只有一个对象。
 *如何保证类在内存中只有一个对象呢?
	(1)控制类的创建,不让其他类来创建本类的对象。private 
	(2)在本类中定义一个本类的对象。private static/final
	(3)提供公共的访问方式。  public static ****(){return s}
* 单例主要写法两种设计模式,还有一种无名.	

public class a {

	/**
	 * @param args
	 * 单例设计模式:保证类在内存中只有一个对象。
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//Singleton s1 = new Singleton();
		Singleton s1 = Singleton.s;//成员变量被私有,不能通过类名.调用
		//Singleton.s = null;//禁止成员变量被修改,private修饰
		Singleton s2 = Singleton.s;		
		System.out.println(s1 == s2);//true	
		
		/*Singleton s1 = Singleton.getInstance();
		Singleton s2 = Singleton.getInstance();
		
		System.out.println(s1 == s2);*/
		
		}
}

/**
 * 饿汉式
 * **/
/*class Singleton {
	//1,私有构造方法,其他类不能访问该构造方法了
	private Singleton(){}
	//2,创建本类对象
	//public static Singleton s = new Singleton();
	private static Singleton s = new Singleton();
	//3,对外提供公共的访问方法
	public static Singleton getInstance() {	//获取实例
		return s;
	}
}*/


/**
 * 懒汉式,单例的延迟加载模式,开发时一般不用,面试用,多线程访问时有安全隐患
 * **/
/*class Singleton {
	//1,私有构造方法,其他类不能访问该构造方法了
	private Singleton(){}
	//2,声明一个引用
	private static Singleton s ;
	//3,对外提供公共的访问方法
	public static Singleton getInstance() {	//获取实例
		if(s == null) {//什么时候用什么时候创建
			//线程1等待,线程2等待
			s = new Singleton();
		}
		return s;
	}
}*/

//第三种,无名
class Singleton {
	//1,私有构造方法,其他类不能访问该构造方法了
	private Singleton(){}
	//2,声明一个引用
	public static final Singleton s = new Singleton();
	//final是最终的意思,被final修饰的变量不可以被更改
	
}


饿汉式和懒汉式的区别
* 1,饿汉式是空间换时间,懒汉式是时间换空间
* 2,在多线程访问时,饿汉式不会创建多个对象,而懒汉式有可能会创建多个对象


2.单例设计模式饿汉式的应用——Runtime


Runtime

java.lang.Object
——java.lang.Runtime
public class Runtimeextends Object
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。
可以通过 getRuntime 方法获取当前运行时。 
应用程序不能创建自己的 Runtime 类实例。 

public Process exec(String command) throws IOException
在单独的进程中执行指定的字符串命令。 
对于 exec(command) 形式的调用而言,其行为与调用 exec(command, null, null) 完全相同。 
参数:
command - 一条指定的系统命令。 
返回:
一个新的 Process 对象,用于管理子进程 


public class b {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		
		Runtime r = Runtime.getRuntime();//获取运行时对象
		//r.exec("shutdown -s -t 300");//win7,dos,cmd,命令测试 ,5min后关机
		r.exec("shutdown -a");
	}
}



3.类 Timer

java.lang.Object
——java.util.Timer
public class Timer extends Object
	一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。 
	对 Timer 对象最后的引用完成后,并且 所有未处理的任务都已执行完成后,
计时器的任务执行线程会正常终止(并且成为垃圾回收的对象)。
但是这可能要很长时间后才发生。默认情况下,任务执行线程并不作为守护线程 来运行,所以它能够阻止应用程序终止。
如果调用者想要快速终止计时器的任务执行线程,那么调用者应该调用计时器的 cancel 方法。 

如果意外终止了计时器的任务执行线程,例如调用了它的 stop 方法,
那么所有以后对该计时器安排任务的尝试都将导致 IllegalStateException,就好像调用了计时器的 cancel 方法一样。 

实现注意事项:所有构造方法都启动计时器线程。 

public void schedule(TimerTask task, Date time)
	安排在指定的时间执行指定的任务。
	如果此时间已过去,则安排立即执行该任务。 

public void schedule(TimerTask task, Date firstTime,long period)
	安排指定的任务在指定的时间开始进行重复的固定延迟执行。
	以近似固定的时间间隔(由指定的周期分隔)进行后续执行。 


3.1类 TimerTask
java.lang.Object
——java.util.TimerTask
  所有已实现的接口: Runnable 
public abstract class TimerTask extends Object implements Runnable
由 Timer 安排为一次执行或重复执行的任务。

protected TimerTask()创建一个新的计时器任务。 
public abstract void run()此计时器任务要执行的操作。 
public boolean cancel()取消此计时器任务。

3.2类 Date
java.lang.Object
——java.util.Date
所有已实现的接口: 
Serializable, Cloneable, Comparable<Date> 
直接已知子类: Date, Time, Timestamp 
public class Date extends Objectimplements Serializable, Cloneable, Comparable<Date>
类 Date 表示特定的瞬间,精确到毫秒。 

在类 Date 所有可以接受或返回年、月、日期、小时、分钟和秒值的方法中,将使用下面的表示形式: 
年份 y 	由整数 y - 1900 表示。 
月份		由从 0 至 11 的整数表示;0 是一月、1 是二月等等;因此 11 是十二月。 
日期		(一月中的某天)按通常方式由整数 1 至 31 表示。 
小时		由从 0 至 23 的整数表示。因此,从午夜到 1 a.m. 的时间是 0 点,从中午到 1 p.m. 的时间是 12 点。 
分钟		按通常方式由 0 至 59 的整数表示。 
		由 0 至 61 的整数表示;值 60 和 61 只对闰秒发生,尽管那样,也只用在实际正确跟踪闰秒的 Java 实现中。


public class c {

	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Timer t = new Timer();
		//在指定时间安排指定任务
		//第一个参数,是安排的任务,第二个参数是执行的时间,第三个参数是过多长时间再重复执行
		t.schedule(new Mytimetask(), new Date(117, 4, 17, 11, 02, 50),3000);
		
		while(true) {
			Thread.sleep(1000);
			System.out.println(new Date());
		}
		
	}

}

class Mytimetask extends TimerTask{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("起床背英语单词");
	}
}



4.两个线程间的通信

 先说Object的几个方法

public final void wait() throws InterruptedException
	在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待

public final void notify()
 	唤醒在此对象监视器上等待的单个线程。
	 如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。
	 选择是任意性的,并在对实现做出决定时发生。
	线程通过调用其中一个 wait 方法,在对象的监视器上等待。

public final void notifyAll()
	唤醒在此对象监视器上等待的所有线程。
             

1.什么时候需要通信
	* 多个线程并发执行时, 在默认情况下CPU是随机切换线程的
	* 如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
2.怎么通信
	* 如果希望线程等待, 就调用wait()
	* 如果希望唤醒等待的线程, 就调用notify();
	* 这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用


public class d {

	/**
	 * @param args
	 * 等待唤醒机制
	 */
	public static void main(String[] args) {
		
		final Printer p = new Printer();
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print1();
					} catch (InterruptedException e) {	
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print2();
					} catch (InterruptedException e) {	
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
}

//等待唤醒机制
class Printer {
	
	private int flag = 1;
	
	public void print1() throws InterruptedException {							
		synchronized(this) {
			if(flag != 1) {
				this.wait();//当前线程等待
			}
			System.out.print("我");
			System.out.print("是");
			System.out.print("路");
			System.out.print("人");
			System.out.print("甲");
			System.out.print("\r\n");
			flag = 2;
			this.notify();//随机唤醒单个等待的线程
		}
	}
	
	public void print2() throws InterruptedException {
		synchronized(this) {
			if(flag != 2) {
				this.wait();
			}
			System.out.print("倩");
			System.out.print("女");
			System.out.print("幽");
			System.out.print("魂");
			System.out.print("\r\n");
			flag = 1;
			this.notify();
		}
	}
}



5.三个或三个以上间的线程通信

* 多个线程通信的问题

	* notify()方法是随机唤醒一个线程
	* notifyAll()方法是唤醒所有线程
	* JDK1.5之前无法唤醒指定的一个线程
	* 如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件


public class e {

	/**
	 * @param args
	 * 等待唤醒机制
	 */
	public static void main(String[] args) {
		
		final Printer p = new Printer();
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print1();
					} catch (InterruptedException e) {	
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print2();
					} catch (InterruptedException e) {	
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print3();
					} catch (InterruptedException e) {	
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
}

//等待唤醒机制
class Printer {
	
	private int flag = 1;
	
	public void print1() throws InterruptedException {							
		synchronized(this) {
			if(flag != 1) {
				this.wait();//当前线程等待
			}
			System.out.print("我");
			System.out.print("是");
			System.out.print("路");
			System.out.print("人");
			System.out.print("甲");
			System.out.print("\r\n");
			flag = 2;
			//this.notify();//随机唤醒单个等待的线程
			this.notifyAll();
		}
	}
	
	public void print2() throws InterruptedException {
		synchronized(this) {
			if(flag != 2) {
				this.wait();
			}
			System.out.print("倩");
			System.out.print("女");
			System.out.print("幽");
			System.out.print("魂");
			System.out.print("\r\n");
			flag = 3;
			//this.notify();
			this.notifyAll();
		}
	}
	
	public void print3() throws InterruptedException {
		synchronized(this) {
			if(flag != 3) {
				this.wait();//线程3在此等待,if语句是在哪里等待,就在哪里起来
				//如果改成while循环,每次都会判断标记,程序有可能死掉,都等待
				//while + notifyAll唤醒所有线程解决,1.5版本之间
			}
			System.out.print("易");
			System.out.print("飞");
			System.out.print("扬");
			System.out.print("飯");
			System.out.print("\r\n");
			flag = 1;
			//this.notify();
			this.notifyAll();
		}
	}
}

标题4、5总结

1,在同步代码块中,用哪个对象锁,就用哪个对象调用wait方法
2,为什么wait方法和notify方法定义在Object这类中?
 * 	因为锁对象可以是任意对象,Object是所有的类的基类,所以wait方法和notify方法需要定义在Object这个类中
3,sleep方法和wait方法的区别?
 * a,sleep方法必须传入参数,参数就是时间,时间到了自动醒来
 *   wait方法可以传入参数也可以不传入参数,传入参数就是在参数的时间结束后等待,不传入参数就是直接等待
 * b,sleep方法在同步函数或同步代码块中,不释放锁,睡着了也抱着锁睡
 * 	 wait方法在同步函数或者同步代码块中,释放锁,否则,cou一直在此等待,谁都无法执行




6.JDK1.5的新特性互斥锁


ReentrantLock

java.lang.Object
——java.util.concurrent.locks.ReentrantLock
所有已实现的接口: Serializable, Lock 
public class ReentrantLock extends Object implements Lock, Serializable
一个可重入的互斥锁 Lock,
它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 


public void lock()
	获取锁。 
	如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。
	如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回。
	如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,
	该线程将一直处于休眠状态,此时锁保持计数被设置为 1。

public void unlock()
	试图释放此锁。 
	如果当前线程是此锁所有者,则将保持计数减 1。如果保持计数现在为 0,则释放该锁。

public Condition newCondition()
	返回用来与此 Lock 实例一起使用的 Condition 实例。 


接口 Condition(条件,情况的意思)

java.util.concurrent.locks 
	Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,
以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 。

***理解要点*****
其中,ReentrantLock替代了 synchronized同步代码块和方法的使用,
Condition 替代了 Object 监视器 wait,notify,notifyA方法的使用。****
对于上面这两各类,网上看了很多讲解,讲的很复杂深入,却不容易让人理解。其实抓住这两点替代关系就很容易明了			

 

Condition 实例实质上被绑定到一个锁上。
要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。


void await() throws InterruptedException
	造成当前线程在接到信号或被中断之前一直处于等待状态。 


void signal()
	唤醒一个等待线程。(可以唤醒指定条件的线程) 
	如果所有的线程都在等待此条件,则选择其中的一个唤醒。
	在从 await 返回之前,该线程必须重新获取锁。 


简述

1.同步
	* 使用ReentrantLock类的lock()和unlock()方法进行同步
2.通信
	* 使用ReentrantLock类的newCondition()方法可以获取Condition对象
	* 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
	* 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了



public class f {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		final Printer p = new Printer();
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print1();
					} catch (InterruptedException e) {	
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print2();
					} catch (InterruptedException e) {	
						e.printStackTrace();
					}
				}
			}
		}.start();
		
		new Thread() {
			public void run() {
				while(true) {
					try {
						p.print3();
					} catch (InterruptedException e) {	
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
}


class Printer {
	
	private ReentrantLock r = new ReentrantLock();//初始化可重入锁
	private Condition c1 = r.newCondition();//创建一个锁的条件c1
	private Condition c2 = r.newCondition();
	private Condition c3 = r.newCondition();
	
	private int flag = 1;
	
	public void print1() throws InterruptedException {							
		r.lock();			//获取锁。		
			if(flag != 1) {
				c1.await();//c1条件的锁线程等待
			}
			System.out.print("我");
			System.out.print("是");
			System.out.print("路");
			System.out.print("人");
			System.out.print("甲");
			System.out.print("\r\n");
			flag = 2;
			c2.signal(); //唤醒c2条件的线程,使程序从c2.await()处继续执行
			//Condition条件的意思,但他替代的是Object线程监视器的作用
		r.unlock();		  //试图释放此锁。						
		
	}
	
	public void print2() throws InterruptedException {
		r.lock();
			if(flag != 2) {
				c2.await();
			}
			System.out.print("倩");
			System.out.print("女");
			System.out.print("幽");
			System.out.print("魂");
			System.out.print("\r\n");
			flag = 3;
			c3.signal();
		r.unlock();
	}
	
	public void print3() throws InterruptedException {
		r.lock();
			if(flag != 3) {
				c3.await();
			}
			System.out.print("易");
			System.out.print("飞");
			System.out.print("扬");
			System.out.print("飯");
			System.out.print("\r\n");
			flag = 1;
			c1.signal();
		r.unlock();
	}
}





7.线程组的概述和使用

* A:线程组概述
	* Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
	* 默认情况下,所有的线程都属于主线程组。
		* public final ThreadGroup getThreadGroup()	//通过线程对象获取他所属于的组
		* public final String getName()				//通过线程组对象获取他组的名字
	* 我们也可以给线程设置分组
		* 1,ThreadGroup(String name) 			//创建线程组对象并给其赋值名字
		* 2,创建线程对象
		* 3,Thread(ThreadGroup?group, Runnable?target, String?name) 
		* 4,设置整组的优先级或者守护线程
B:案例演示


public class g {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		/*
		MyRunnable mr = new MyRunnable();
		Thread t1 = new Thread(mr, "张三");
		Thread t2 = new Thread(mr, "李四");
		
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();
		
		System.out.println(tg1.getName());//默认的是主线程组
		System.out.println(tg2.getName());//main
		// 通过结果我们知道了:线程默认情况下属于main线程组
		// 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组
		System.out.println(Thread.currentThread().getThreadGroup().getName());*/		
		
		
		/**
		 * 自己设定线程组
		 * 
		 */
		ThreadGroup tg = new ThreadGroup("我是一个新的线程组");	//创建新的线程组
		MyRunnable mr = new MyRunnable();					//创建Runnable的子类对象
		
		Thread t1 = new Thread(tg, mr, "张三");			//将线程t1放在组tg中
		Thread t2 = new Thread(tg, mr, "李四");			//将线程t2放在组tg中
		
		System.out.println(t1.getThreadGroup().getName());//获取组名
		System.out.println(t2.getThreadGroup().getName());//我是一个新的线程组
		//通过组名称设置后台线程,表示该组的线程都是后台线程
		tg.setDaemon(true);//tg设置为守护线程组

	}

}

class MyRunnable implements Runnable {

	@Override
	public void run() {
		for(int i = 0; i < 1000; i++) {
			System.out.println(Thread.currentThread().getName() + "...." + i);
		}
	}
	
}


8.线程的五种状态

新建,就绪,运行,阻塞,死亡



9.线程池的概述和使用


 A:线程池概述

	* 程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
	* 而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
	* 线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
	* 在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池

 B:内置线程池的使用概述

	* JDK1.5新增了一个Executors工厂类来产生线程池,有如下几个方法
	* 使用步骤:
		* 创建线程池对象
		* 创建Runnable实例
		* 提交Runnable实例
		* 关闭线程池
	* 使用步骤:
		* 创建线程池对象
		* 创建Runnable实例
		* 提交Runnable实例
		* 关闭线程池



Executors

java.lang.Object
——java.util.concurrent.Executors
public class Executors extends Object
此包中所定义的 Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 和 Callable 类的工厂和实用方法。
从以下版本开始: 1.5 


public static ExecutorService newFixedThreadPool(int nThreads)
	创建一个可重用固定线程数的线程池
	返回:新创建的线程池 


public static ExecutorService newSingleThreadExecutor()
	创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

这些方法的返回值是ExecutorService对象,


接口 ExecutorService

所有超级接口: Executor 
所有已知子接口: ScheduledExecutorService 
所有已知实现类: 
AbstractExecutorService, ScheduledThreadPoolExecutor, ThreadPoolExecutor 
public interface ExecutorService extends Executor
该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程

void shutdown()
	启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。 

Future<?> submit(Runnable task)
	提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
	该 Future 的 get 方法在成功 完成时将会返回 null。 
	参数:task - 要提交的任务 
	返回:表示任务等待完成的 Future 

<T> Future<T> submit(Callable<T> task)
	提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
	该 Future 的 get 方法在成功完成时将会返回该任务的结果。


接口 Future<V>

类型参数:
V - 此 Future 的 get 方法所返回的结果类型
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。


V get() throws InterruptedException, ExecutionException
	如有必要,等待计算完成,然后获取其结果。 


接口 Callable<V>

类型参数:
V - call 方法的结果类型
返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。 
Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。
但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。 


V call() throws Exception
	计算结果,如果无法计算结果,则抛出一个异常


案例一

public class i {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExecutorService pool = Executors.newFixedThreadPool(2);//创建线程池
		pool.submit(new MyRunnable());	//将线程放进池子里并执行
		pool.submit(new MyRunnable());
		
		pool.shutdown();				//关闭线程池
	}

}

class MyRunnable implements Runnable {

	@Override
	public void run() {
		for(int i = 0; i < 1000; i++) {
			System.out.println(Thread.currentThread().getName() + "...." + i);
		}
	}
	
}


案例二

多线程程序实现的方式3
提交的是Callable
好处和弊端
	* 好处:
		* 可以有返回值
		* 可以抛出异常
		
	* 弊端:
		* 代码比较复杂,所以一般不用



public class j {

	/**
	 * 多线程程序实现的方式3
	 * @param args
	 * @throws ExecutionException 
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService pool = Executors.newFixedThreadPool(2);//创建线程池
		Future<Integer> f1 = pool.submit(new MyCallable(100));//将线程放进池子里并执行
		Future<Integer> f2 = pool.submit(new MyCallable(50));
		
		System.out.println(f1.get());
		System.out.println(f2.get());
		
		pool.shutdown();//关闭线程池
	}

}

class MyCallable implements Callable<Integer> {
	private int num;
	public MyCallable(int num) {
		this.num = num;
	}
	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for(int i = 1; i <= num; i++) {
			sum += i;
		}	
		return sum;
	}	
}








  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值