Java自学课程笔记7
2021年2月25日21点35分 JAVA自学课程笔记7: 数组: 创建并使用引用类型数组: public class Test1{ public static void main(String[] args){ MyDate[] m; m = new MyDate[10]; for(int i=0; i<10; i++){ m[i] = new MyDate(i+1, i+1, 1990+i); m[i].display(); } } } //m就是MyDate数据类型的数组,通过m[i] = new MyDate创建每一个MyDate对象。 引用类型元素组成的一维数组在使用过程中一般存在着两级的指向关系,这是理解多维不等长数组的基础。 例如: int[][] xx = new int[2][3];//等长数组 int[][] xx = {{3,2,7},{1,5},{6}};//不等长数组 C语言中无不等长数组,但可以通过指针实现: #include <stdio.h> int main(){ int* ipArr[3]; int iArra[] = {1, 2, 3}; int iArrb[] = {4, 5}; ipArr[0] = iArra; ipArr[1] = iArrb; printf("%d\n",ipArr[1][2]); return 0; } //即用int型数组指针数组里的每个int型数组指针指向不同长度的数组即可。 数组拷贝: public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)方法: 引用:System.arraycopy(a, 0, b, 1, 2); //b数组从1开始2个长度的元素赋给了a数组从0开始的位置。 Test2: public static void main(String[] args){ int[] a = {1, 2, 3, 4, 5}; int[] b = {-1, -2, -3, -4, -5}; System.arraycopy(a, 0, b, 1, 2); System.out.println("a = "); for(int i=0; i<a.length; i++){ System.out.println(a[i]); } System.out.println("b = "); for(int i=0; i<b.length; i++){ System.out.println(b[i]); } } public static void sort(int[] a)方法: 使用:Arrays.sort(); //对指定的 int 型数组按数字升序进行排序。 Test3: public static void main(String[] args){ int[] data = {2, 1, 5, 6, 9, 8, 4}; System.out.println("排序前data内容:"); showArray(data); Array.sort(data); System.out.println("排序后data内容:"); showArray(data); } public static void showArray(int[] data){ for(int e : data) System.out.printf("%d\t",e); //'\t' == Tab //每循环一次,将data的一个值赋给e,data中的元素个数决定循环次数。 System.out.println(""); } //运行结果: 排序前data内容: 2 1 5 6 9 8 4 排序后data内容: 1 2 4 5 6 8 9 单道程序设计环境中程序特点: 单道程序设计环境是指:计算机中除了操作系统之外,只存在一个用户程序,即户程序独享整个计算机资源。 单道程序有如下特点: 1. 资源的独占性:任何时候,位于内存中的程序可以使用系统中的一切资源,不可能有其他程序与之竞争。 2.执行的顺序性:内存中每次只有一个程序,各个程序是按次序执行的,即做完一个,再做下一个。(绝对不可能出现在一个程序运行过程中,又夹杂进另一个程序执行的现象存在。) 3.结果的再现性:只要执行环境和初始条件相同,重复执行一个程序,获得的结果总是一样的。 多道程序设计环境中程序特点: 多道程序设计是指:计算机中除了操作系统之外,存在多个用户程序,这些程序同时运行。 多道程序设计有如下特点: 1.间断性:由于资源共享和合作,并发程序间相互制约,造成合作执行间断。 2.失去封闭性:程序执行受外界影响。 3.不可再现性:重复执行时,可能得到不同结果。 进程的由来: 一方面为了保持程序“是一个在时间上严格有序的指令集合,是静态的保存在存储介质上”这个概念的原有含义,另一方面为了刻画多个程序共同运行时呈现出的这些特征,在操作系统中,以“程序”为基础,又引入了“进程”这一新的概念! 通俗点说,为了不破坏“程序"这个词原有的含义,而又能刻画多个程序共同运行时呈现出的新特征,所以引入了进程这一概念,按照大多数教材的说法:“为了使程序能并发执行,且为了对并发执行的程序加以描述,所以人们引入了进程”。 (一个记事本程序,三个进程,即三个.txt文件同时打开。) 进程的定义没有统一的说法,进程是程序在某个数据集上的执行,是一个动态的实体。 线程(Thread): 线程是一个程序里的不同执行路径。 以前所编写的程序,每个程序都有一个入口、一个出口以及一个顺序执行的序列,在程序执行过程中的任何指定时刻,都只有一个单独的执行点。 事实上,在单个程序内部是可以在同一时刻进行多种运算的,这就是所谓的多线程。 程序、进程、线程的异同参见操作系统,这些并不影响我们对Java的学习。 多线程的优势:编程简单,效率高(能直接共享数据和资源,多进程不能。)、适用于开发服务程序(Web服务,聊天服务等)。 创建一个新线程: 1.创建一个继承Thread的类 class A extends Thread{} 2.构造一个该类的对象aa A aa = new A(); 3.调用aa的start()方法(由Thread继承过来,start()方法会创建一个新的线程,并自动调用该对象中的run()方法,且仅调用run()方法) aa.start(); 执行一个线程相当于执行该线程run()方法中的代码。 执行完aa.start();后并不表示aa所对应的线程就一定会立即得到了执行,aa.start();执行完后只是表示aa线程具有了可以立即被CPU执行的资格,但由于想抢占CPU执行的线程很多,CPU并不一定会文即去执行aa所对应的线程。 一个Thread对象能且只能代表一个线程,即不可调用两次start()方法,否则会抛出java.lang.IllegalThreadStateException异常。 创建线程的第二种方法: 1.定义一个实现了Runnable接口的类,假定为A class A implements Runnable{} 2.创建A类对象aa A aa = new A(); 3.利用aa构造一个Thread对象tt Thread tt = new Thread(aa); 4.调用tt中的start()方法 tt.start(); 这种方法可以只通过一个类创建多个线程,启动线程只需要tt1.start();和tt2.start();就好了。 “此外,Runnable 为非 Thread 子类的类提供了一种激活方式。通过实例化某个 Thread 实例并将自身作为运行目标,就可以运行实现 Runnable 的类而无需创建 Thread 的子类。大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。 ” Runnable接口下的方法有void run(),而Thread类下的方法有public void run()。Runnable下实现run()方法通过重写为public void run()。 Thread常用方法: public final void setName(String name) 设置当前线程名字 public static Thread currentThread() 返回对当前正在执行的线程对象的引用 public final String getName() 返回当前线程的名字 例如:System.out.printf("%s正在执行!\n", Thread.currentThread().getName()); //运行结果:main正在执行! Test4: class A extends Thread{ public void run(){ System.out.printf("%s正在执行!\n", Thread.currentThread().getName()); } } public class Test4{ public static void main(String[] args){ A aa1 = new A(); aa1.setName("1stThread"); aa1.start(); A aa2 = new A(); aa2.setName("2ndThread"); aa2.start(); A aa3 = new A(); aa3.setName("3rdThread"); aa3.start(); System.out.printf("%s正在执行!\n", Thread.currentThread().getName()); } } //运行结果: 1stThread正在执行! 3rdThread正在执行! main正在执行! 2ndThread正在执行! (以上不分顺序,都有可能靠前靠后!) 线程控制的基本方法: isAlive() 判断线程是否还"活"着,即线程是否还未终止。 getPriority() 获得线程的优先级数值。 setPriority() 设置线程的优先级数值。 Thread.sleep() 将当前线程睡眠指定毫秒数。 join() 调用某线程的该方法,将当前线程与该线程“合并",即等待该线程结束,再恢复当前线程的运行。 yield() 让出CPU,当前线程进入就绪队列等待调度。 wait() 当前线程进入对象的wait pool。 notify() / notifyA1l() 唤醒对象的wait poo1中的一个/所有等待线程。 线程的优先级(priority): (针对就绪状态的所有线程,范围从1到10,缺省是5) Thread.MIN_PRIORITY == 1 Thread.MAX_PRIORITY == 10 Thread.NORM_PRIORITY == 5 通过下述方法获得或设置线程对象的优先级: int getPriority(); void setPriority(int newPriority); 通常高优先级的线程将先于低优先级的线程执行,但不总是这样,因此实际开发中并不单纯依赖优先级来决定线程运行次序。 线程的休眠(sleep): 暂停*当前运行中的线程,使之进入*阻塞状态,待经过指定的“延迟时间”后再醒来并转入到*就绪状态。 Thread类提供的相关方法: public static void sleep(long millis) public static void sleep(long millis, int nanos) //millis毫秒,nanos纳秒 由于是静态方法,可以由Thread直接调用。 sleep()方法会抛出InterruptedException异常,我们必须要进行捕捉。 public class Test5{ public static void main(String[] args){ A aa = new A(); Thread tt = new Thread(aa); tt.start(); } } class A implements Runnable{ public void run(){ for(int i=0; i<10; i++){ System.out.println(Thread.currentThread().getName()+" "+i); try{ Thread.sleep(1000); //这里的Thread.sleep()会抛出异常,必须进行捕捉,不能在run()函数的 后面添加“throws Exception” }catch(Exception e){ } } } } 无论是继承Thread类的run()方法还是实现Runnable接口的run()方法,都不能抛出任何异常: class A implements Runnable{ public void run() /*throws Exception*/{ } } class B extends Thread{ public void run() /*throws Exception*/{ } } 因为:重写方法抛出异常的范围不能大于被重写方法抛出的异常范围。(即子类抛出异常范围不能比父类大。)(属于多态的知识点,为了能实现多态。) Runnable和Thread中的run()方法都没有抛出异常。 线程的让步(yield): 让出CPU,给其他线程执行的机会。 即让*运行中的线程放弃当前获得CPU处理的机会,但不是使该线程阻塞,而是转入*就绪状态。 声明:public static void yield() Test6: public class Test6{ public static void main(String[] args){ MyThread mt = new MyThread(); Thread t1 = new Thread(mt); Thread t2 = new Thread(mt); t1.setName("线程A"); t2.setName("线程B"); t1.start(); t2.start(); } } class MyThread implements Runnable{ public void run(){ for(int i=1; i<=100; i++){ System.out.println(Thread.currentThread().getName()+" "+i); if(i%10 == 0) Thread.yield(); } } } join()方法: Test7: public class TestJoin { public static void main(String[] args){ MyRunner r = new MyRunner(); Thread t= new Thread(r); t.start(); try{ System.out.println(Thread.currentThread().getName()); //输出结果为:“main”,说明主线程在执行t.join()方法。 t.join(); //第7行 }catch(InterruptedException e){ e.printStackTrace(); } for(int i=0;i<50;i++){ System.out.println("主线程:" + i); } } } class MyRunner implements Runnable { public void run() { for(int i=0;i<50;i++){ System.out.println("子线程:"+ i); } } } //第七行的“t.join();”暂停当前正在执行“t.join();”的线程(主线程),直到t所对应的线程运行终止之后,当前线程才会获得继续执行的机会。 注意:t.join()不是暂停t对象所对应的线程。 //运行结果: *先执行子线程:从1到49,再执行主线程:从1到49。 (若无join()方法语句,则会主、子线程交替执行。) this.notify(): 不是叫醒正在执行this.notify()的线程,而是叫醒一个现在正在“wait” this对象的其他线程(即叫醒当前一个因为this.wait()方法阻塞的线程),如果有多个线程正在“wait” this对象,通常是叫醒最先“wait” this对象的线程,但具体是叫醒哪一个由系统调度器控制,程序员无法控制。 “未雨绸缪性”: 假设现在有t1,t2,t3,t4四个线程,我们在t4线程执行了aa.notify()语句。则即便此时t1,t2,t3没有一个线程因为“wait” aa对象而陷入阻塞状态,t4线程中执行aa.notify方法也不会有任何错误。(即执行aa.notify()方法时如果一个线程都没有被叫醒,这是可行的。) aa.wait(): 将执行aa.wait()的当前线程转入阻塞状态,让出CPU的控制权,释放对aa的锁定(Java自学课程笔记8中的synchronized)(此外,已过时的suspend()方法无法取消对aa的锁定,所以不提倡使用。) aa.notify(): 假设执行aa.notify()的当前线程为t1,如果当前时刻有其他线程因为执行了aa.wait()而陷入阻塞状态,则叫醒其中的一个,即将该因“wait”而陷入阻塞状态的线程转为就绪状态。 aa.notifyAll(): 叫醒其他所有因为执行了aa.wait()而陷入阻塞状态的线程。