黑马程序员----JAVA基础----函数与数组及多线程2

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

一、函数

1,函数的定义:函数是定义在类中的具有特定功能的一段独立小程序,又称为方法。

2,函数的格式:

修饰符 返回值类型 函数名(参数类型 形式参数1,参数类型 形式参数2,。。。){

执行语句;

return 返回值;

}

注意1:当函数没有返回值时,返回值类型用void表示,函数体内的return语句可以不写。

如果写的话,后面不用加返回值,直接跟‘;’。

注意2:在return语句下面不能再有语句。

3,函数的特点:

a,定义函数可以将功能代码进行封装,可以方便对功能进行使用,提高了复用性。

b,函数只有在被调用时才会执行

在使用函数时,要明确函数的参数列表,要明确函数的返回值类型。

4,函数的一个重要特性:重载

在同一个类中,允许存在一个以上的同名函数,只要它们的参数列表不同即可。

参数列表包括参数个数、参数类型、参数顺序,有一项不同,函数就不同。

重载与返回值类型无关,只与参数名和参数列表有关。

Java是严谨性语言,如果函数出现调用的不确定性就会失败。

演示代码:

/*
 * 重载:同一个类中,允许存在一个以上的同名函数,只要它们的参数列表不同即可
 */
public class FunctionDemo {

	public static void main(String[] args) {

	}
	// 返回值:int  参数列表:2个,都是int
	private static int add(int a,int b){
		return a+b;
	}
	// 返回值:int  参数列表:3个,都是int
	private static int add(int a,int b,int c){
		return a+b+c;
	}
	// 返回值:int  参数列表:2个,都是double
	private static int add(double a,double b){
		return (int)(a+b);
	}
	// 返回值:double  参数列表:2个,都是int	与上面的函数相同。
	// 虽然返回值类型不同,但函数名与参数列表相同,
	// java是严谨性语言,不允许这种形式存在
//	private static double add(int a,int b){
//		return	(double)(a+b);
//	}
}

二、数组

1,数组的概念:同一种类型数据的集合。

2,数组的好处:可以给数组中的元素进行编号,从0开始。方便操作这些元素。

3,数组的格式:元素类型[ ] 数组名 = new  元素类型[元素个数或数组长度] 。

数组一旦建立,必须明确长度。演示代码:

/*
 * 数组的定义格式
 */
public class ArrayDemo1 {

	public static void main(String[] args) {
		// 数组定义时必须明确数组的长度<span style="white-space:pre">	</span>,需要一个容器,但是不明确容器的具体数据
		int[] arr1 = new int[3];
		int arr2[] = new int[3];
		// 数组定义并初始化,不要在[]填写长度,存储已知具体数据
		int[] arr3 = new int[]{3,2,1};
		int arr4[] = new int[]{2,4,6,8};
		// 方式三	静态初始化
		int[] arr5 = {33,44,55,66};
	}

}

4,内存划分:寄存器、本地方法区、方法去、 栈内存、堆内存

栈内存:存储的是局部变量。变量所属的作用域一旦消失,变量就会释放。

堆内存:存储数组和对象(数组也是对象),凡是被new出来的都在堆内存。

对内存的特点:a,每一个实体都有首地址值。b,对内存中每一个变量都有默认化初始值,

根据类型的不同而不同。整数--0,小数--0.0或0.0f,boolean--false,char--\u0000

c,对内存中的对象不用后会变成垃圾,java会自动回收垃圾(垃圾回收机制)。

5,数组使用过程中一些常见问题:

a,当访问一个数组中不存在的角标时,会引发角标越界异常:ArrayIndexOutOfBoundsException

b,当引用型变量没有任何指向时,还在用其操作实体,会引起NullPointerException

5,数组的基本操作:核心思想就是对角标的操作。

数组的基本操作包括存入、取出、获得最大(小)值、排序(升/降)、二分查找等,演示代码:

public class ArrayDemo1 {

	public static void main(String[] args) {
		show1();	// 数组的存取
		show2();	// 数组的最大值和最小值
		show3();	// 选择排序
		show4();	// 冒泡排序
		show5();	// 二分查找
	}
	
	private static void show5() {
		int[] arr = new int[]{33,22,11,44,66,55};
		int index = binarySearch(arr,44);
		index = binarySearch2(arr,66);
		System.out.println(index);
		
	}
	private static int binarySearch2(int[] arr,int key){<span style="white-space:pre">	</span>// 二分查找第二种形式
		int min,mid,max;
		min = 0;
		max = arr.length-1;
		while(min<max){
			mid = (min+max)>>1;
			if(key>arr[mid])
				min = mid+1;
			else if(key<arr[mid])
				max = mid-1;
			else
				return mid;
				
		}
		
		return -1;
	}
	private static int binarySearch(int[] arr,int key) {
		int min,mid,max;
		min = 0;
		max = arr.length-1;
		mid = (min+max)/2;
		while(arr[mid]!=key){
			if(key>arr[mid])
				min = mid+1;
			else if(key<arr[mid])
				max = mid-1;
			if(min>max)
				return -1;
			mid = (max+min)/2;
		
		}
		return mid;
	}

	private static void show4() {
		int[] arr = new int[]{33,22,11,44,66,55};
		bubbleSort(arr);
	}
	/*
	 * 冒泡排序
	 * 思路:遍历数组,第一轮:从第一个值(0角标)开始,与第二个值比较,如果第一个值 大于
	 * 第二个值,将二者位置交换,反之则不交换。然后用第二个值与第三个值比较,依次类推
	 * 当遍历到倒数第二个值时,就能确定最后一个值时数组中的最大值。
	 * 然后从头开始遍历,开始第二轮排序,此时不用考虑最后一个元素,这样额可以
	 * 确定倒数第二个角标的数值。
	 * 然后依次循环即可。
	 */
	private static void bubbleSort(int[] arr) {
		// 确定外循环的次数
		for(int i=0; i<arr.length-1; i++){
			// -1 避免角标越界
			// -i使内循环每次参与比较的值递减
			for(int j=0; j<arr.length-1-i; j++){
				if(arr[j]>arr[j+1]){
					int temp = arr[j];
					arr[j] = arr[j+1];
					arr[j+1] = temp;
				}
			}
		}
		for(int i=0; i<arr.length; i++){
			System.out.print(arr[i]+" ");
		}
	}
	/*
	 * 数组的排序(升序)
	 * 思路:遍历数组,先将第一个值作为数组的最小值,将第一个值与第二值作比较,
	 * 如果第一个值小于第二个值,那么位置不变,如果第一个值大于第二个值,那么
	 * 将这两个值的位置互换。然后用第一个值和第三个值比较,依次类推
	 * 通过一轮排序后,第一个数值已经是最小值,然后开始从第二个值开始比较,与
	 * 第一轮比较原理一致。通过比较可以确定第二位置的数值。
	 * 第三个、第四个。。。依次类推
	 * 当取到最后一个值时,不需要再判断
	 */
	private static void show3() {
		int[] arr = new int[]{33,22,11,44,66,55};
		selectSort(arr);
		for(int i=0; i<arr.length; i++){
			System.out.print(arr[i]+" ");
		}
	}
	/**
	 * @param arr
	 */
	private static void selectSort(int[] arr) {
		for(int i=0; i<arr.length-1; i++){
			for(int j=i+1; j<arr.length; j++){
				// 如果前一个值大于后一个值,将二者的位置调换
				if(arr[i]>arr[j]){	
					int temp = arr[i];
					arr[i] = arr[j];
					arr[j] = temp;
				}
			}
		}
	}
	/*
	 * 取数组中的最大值
	 * 思路:定义一个变量记录最大值
	 * 遍历数组,先将第一个值默认为最大值,跟后面一个数值进行比较,如果第一个数大于第二个数值,
	 * 那么最大值仍然是第一个数值,否则是第二个数值,依次类推,知道遍历完数组为止
	 */
	private static void show2() {

		int[] arr = {3,2,11,25,99,23,45};
		// 定义方法获取最大值。最小值思路一样
		int max = getMax(arr);
		System.out.println(max);
	}

	private static int getMax(int[] arr) {
		// 注意对max进行初始化时,应该将数组中的一耳光值赋值给max
		int max=arr[1];
		// 遍历数组,当有值比max大时,就将这个值赋值给max
		for(int i=1; i<arr.length; i++){
			if(arr[i]>max)
				max = arr[i];
		}
		return max;
	}
	// 数组的存取
	private static void show1() {
		int[] arr = new int[5];
		// 对数组赋值
		for(int i=0; i<arr.length; i++){
			arr[i] = i;
		}
		// 将数组中的值取出
		for(int i=0; i<arr.length; i++){
			System.out.print("arr["+i+"]="+arr[i]+" ");
		}
	}

}
6,进制转换:

思路(以16进制为例):将数字num与15进行&运算,得到最低4位的数值,再将该数值转换为

相应进制的数字即可,这个转换过程可以根据查表法来完成。然后将数字num右移4位(无符号

右移>>>),如果右移后的数字不为0,再与15进行&运算,依次类推。0为特殊情况,单独列出。

演示代码:

public class ArrayTest {

	public static void main(String[] args) {
		int num = 28;
		toBinary(num);
		toOctal(num);
		toHex(num);
	}

	private static void toHex(int i) {
		trans(i,15,4);
	}

	private static void toOctal(int i) {
		trans(i,7,3);
	}

	private static void toBinary(int i) {
		trans(i,1,1);
	}

	private static void trans(int num, int j, int k) {
		// 当num为0时,直接返回0
		if(num==0){
			System.out.println(num);
			return;
		}
		// 建立一个表,用于查询
		char[] chs = {'0','1','2','3','4','5','6','7','8','9',
				'A','B','C','D','E','F'};
		char[] arr = new char[32];
		int pos = arr.length;
		while(num!=0){
			int temp = num&j;
			arr[--pos] = chs[temp];
			num>>>=k;
		}
		
		for(int i=pos; i<arr.length; i++){
			System.out.print(arr[i]);
		}
		System.out.println();
	}
}
7,二维数组
二维数组与一维数组大致相似,下面代码总结了格式及一些常见问题:

public class ArrayDemo2 {

	public static void main(String[] args) {
		show1();	// 二维数组的定义
		show2();	// 常见问题
		show3();	// 二维数组遍历
	}

	private static void show3() {
		int[][] arr = {{2,3,4},{2,3},{5}};
		for(int i=0; i<arr.length; i++){
			for(int j=0; j<arr[i].length; j++){
				System.out.print(arr[i][j]+" ");
			}
			System.out.println();
		}
	}

	private static void show2() {
		int[][] arr = new int[3][2];
		// 二维数组
		System.out.println(arr);	
		// 打印二维数组中角标为0的一维数组
		System.out.println(arr[0]);	
		// 打印二维数组中角标为0的一维数组中0角标元素
		System.out.println(arr[0][0]);	
		
		int[][] arr2 = new int[3][];
		// 二维数组
		System.out.println(arr);	
		// 打印二维数组中角标为0的一维数组,打印结果为null
		System.out.println(arr2[0]);	
		// 打印二维数组中角标为0的一维数组中0角标元素,会抛出NullPointerException
//		System.out.println(arr2[0][0]);
		
		// 打印二维数组长度,其实就是一维数组的个数
		System.out.println(arr.length);
		// 打印二维数组中角标为1的的一维数组的长度
		System.out.println(arr[1].length);
	}

	private static void show1() {
		// 定义-
		int[][]	arr1 = new int[3][2];
		int arr2[][] = new int[2][3];
		// 定义二
		int[][] arr3 = new int[][]{{1,2,3},{1,2}};
		int arr4[][] = new int[][]{{1,2,3},{1,2}};
		// 定义三
		int arr5[][] = {{1,2,3},{4,5},{6}};
	}

}
三、多线程2

1,同步代码块对锁的操作是隐式的。

jdk1.5后,将锁封装成对象,提供了lock(获取锁)和unlock(释放锁)方法。

ReentrantLock,一个可重入的互斥锁Lock,它具有与使用synchronized相同的作用。

Lock接口:替代了同步代码块或者同步函数,将同步代码块的隐式锁操作变成显式操作。

同时更为灵活,可以在一个锁上加上多个监视器对象。

lock用来获取锁,unlock用来释放锁,通常需要定义在finally代码块中。

Condition接口:替代了Object中的wait、notify、notifyAll方法,将这些监视器方法进行单独

的封装,变成Condition监视器对象,可以任意锁进行组合。演示代码:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadDemo10 {

	public static void main(String[] args) {
		// 创建公共资源的对象
		Duck d = new Duck();
		// 将该对象分别封装到生产和消费线程,再将消费或生产线程的对象作为Thread对象的构造函数
		// 的参数进行传递,并开启线程
		new Thread(new Producer(d)).start();
		new Thread(new Consumer(d)).start();
		new Thread(new Producer(d)).start();
		new Thread(new Consumer(d)).start();
	}

}
class Duck{
	private String name;
	private int count = 1;
	private boolean flag = false;
	// 创建锁对象
	Lock lock = new ReentrantLock();
	Condition pro = lock.newCondition();<span style="white-space:pre">	</span>// 创建监视器对象
	Condition con = lock.newCondition();<span style="white-space:pre">	</span>// 一个锁上可以绑上多个监视器对象
	public  void setDuck(String name){
		lock.lock();
		try{
			while(flag)
				try {
					pro.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			this.name = name+count;
			System.out.println(this.name+"++++++++");
			count++;
			this.flag = true;
			// 使用notify,一次只唤醒一个线程,无法确保唤醒对方线程,如果唤醒本方线程,无意义
			
			con.signal();
		}finally{
			lock.unlock();
		}
		
	}
	
	public  void getDuck(){
		lock.lock();
		try{
			while(!flag)
				try {
					con.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			System.out.println(this.name+"--------");
			this.flag = false;
			pro.signal();
		}finally{
			lock.unlock();
		}
		
	}
}
// 将任务进行封装
class Producer implements Runnable{
	// 将Duck进行封装,确保公共资源的唯一性
	private Duck duck;
	
	public Producer(Duck duck){
		this.duck = duck;
	}
	
	public void run(){
		while(true){
			duck.setDuck("烤鸭");
		}
	}
}

//将任务进行封装
class Consumer implements Runnable{
	// 将Duck进行封装,确保公共资源的唯一性
	private Duck duck;
	
	public Consumer(Duck duck){
		this.duck = duck;
	}
	
	public void run(){
		while(true){
			duck.getDuck();
		}
	}
}
2,wait和sleep的区别

a,wait可以指定时间也可以不指定时间,sleep必须指定时间

b,在同步中,对CPU的执行权和锁的处理不同,

wait:释放执行权,释放锁。sleep:释放执行权,不释放锁。

注意:在同步中,谁拿锁谁执行。其他线程可以是阻塞状态,但不能执行。

3,线程的停止:

a,使用stop方法。b,run方法结束。

怎么控制线程任务的结束呢?任务中都会有循环结构,只要控制住循环就可以结束任务。

控制循环通常用定义标记来完成。演示代码:

public class ThreadDemo11 {

	public static void main(String[] args) {
		// 创建任务对象,并传递给线程对象的构造参数
		StopThread st = new StopThread();
		new Thread(st).start();
		new Thread(st).start();
		// 当满足条件时,将flag置为false
		int num = 1;
		for(int x=1; x<100; x++){
			if(x==50){
				st.setFlag();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"......"+num++);
		}
		System.out.println("over");
	}

}

class StopThread implements Runnable{
	private boolean flag = true;
	public void setFlag(){
		flag = false;
	}
	@Override
	public void run() {
		while(flag){	//定义标记,从而可以结束循环结构
			System.out.println(Thread.currentThread().getName()+"......");
		}
	}
	
}

但是如果线程处于了冻结状态(wait),无法读取标记,该如何结束?

在Thread类中提供了一个interrupt方法,将线程的冻结状态清除掉,让线程具有CPU的执行资格,

但是会抛出一个InterruptedException。

4,守护线程:setDaemon,在线程启动前使用,当正在运行的线程都为守护线程时,java虚拟机退出。

5,join方法:可以加入一个线程。setPriority方法:设置执行优先级,最低为1,最高为10,默认为5。

当线程优先级最高时,并不意味着该线程可以执行完才运行其他线程,依然要和其他线程抢执行权,只不过

运行到的概率提高。

public class ThreadDemo11 {

	public static void main(String[] args) throws InterruptedException {
		// 创建任务对象,并传递给线程对象的构造参数
		StopThread st = new StopThread();
		
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		t1.start();
		t1.join();		// t1线程要申请加入进来运行
		t2.start();
		// 优先级提高,默认优先级为5,最高位10,最低为1
		t2.setPriority(Thread.MAX_PRIORITY);	
		for(int x=1; x<50; x++){
			System.out.println(Thread.currentThread().getName()+"......"+x);
			
		}
		System.out.println("over");
	}

}

class StopThread implements Runnable{
	private boolean flag = true;
	public void setFlag(){
		flag = false;
	}
	@Override
	public void run() {
		for(int x=0; x<50; x++){
			System.out.println(Thread.currentThread().getName()+"......");
		}
		
	}
	
}


The End










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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值