5、多态、异常和多线程


多态
对象的多态性。
class 动物{}
class 猫 extends 动物{}
class 狗 extends 动物{}
猫 x = new 猫();
动物 x = new 猫();//一个对象,两种形态。

猫这类事物即具备者猫的形态,又具备着动物的形态。这就是对象的多态性。 
简单说:就是一个对象对应着不同类型. 

多态在代码中的体现:
父类或者接口的引用指向其子类的对象。
多态的好处:
提高了代码的扩展性,前期定义的代码可以使用后期的内容。
多态的弊端:
前期定义的内容不能使用(调用)后期子类的特有内容。
多态的前提:
1,必须有关系,继承,实现。2,要有覆盖。 


多态时, 成员的特点:
1,成员变量。
编译时:参考引用型变量所属的类中的是否有调用的成员变量,有,编译通过,没有,编译失败。
运行时:参考引用型变量所属的类中的是否有调用的成员变量,并运行该所属类中的成员变量。
简单说:编译和运行都参考等号的左边。哦了。
作为了解。
2,成员函数(非静态)。
编译时:参考引用型变量所属的类中的是否有调用的函数。有,编译通过,没有,编译失败。
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译看左边,运行看右边。因为成员函数存在覆盖特性。
3,静态函数。
编译时:参考引用型变量所属的类中的是否有调用的静态方法。
运行时:参考引用型变量所属的类中的是否有调用的静态方法。
简单说,编译和运行都看左边。
其实对于静态方法,是不需要对象的。直接用类名调用即可。

内部类
定义:是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在。
内部类访问特点:
1,内部类可以直接访问外部类中的成员。
2,外部类要访问内部类,必须建立内部类的对象。

分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容。
这时就是还有的事物定义成内部类来描述。

如果内部类定义了静态成员,则该内部类也必须为静态的。如果内部类是静态的,相当于一个外部类

匿名内部类:就是内部类的简写格式。
必须有前提:内部类必须继承或者实现一个外部类或者接口。
匿名内部类:其实就是一个匿名子类对象。 
格式:new 父类or接口(){子类内容}
当函数参数是接口类型时,而且接口中的方法不超过三个。可以用匿名内部类作为实际参数进行传递

异常
指在运行时期发生的不正常情况。
在java中用类的形式对不正常情况进行了描述和封装对象。
描述不正常的情况的类,就称为异常类。 
其实异常就是java通过面向对象的思想将问题封装成了对象.
用异常类对其进行描述,不同的问题用不同的类进行具体的描述。 比如角标越界。空指针等等。
问题很多,意味着描述的类也很多,将其共性进行向上抽取,形成了异常体系。

最终问题(不正常情况)就分成了两大类。
Throwable:无论是error,还是异常,问题,问题发生就应该可以抛出,让调用者知道并处理。
//该体系的特点就在于Throwable及其所有的子类都具有可抛性,可抛性到底指的是什么呢?怎么体现可抛性呢?其实是通过两个关键字来体现的。throws throw ,凡是可以被这两个关键字所操作的类和对象都具备可抛性.
|--1,一般不可处理的。Error
特点:是由jvm抛出的严重性的问题。这种问题发生一般不针对性处理。直接修改程序
|--2,可以处理的。Exception

该体系的特点:
子类的后缀名都是用其父类名作为后缀,阅读性很想。
  

异常的分类:
1,编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。 
这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。
这样的问题都可以针对性的处理。
2,编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。
这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。
那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。

所以自定义异常时,要么继承Exception。要么继承RuntimeException。

throws 和throw的区别。
1,throws使用在函数上。
   throw使用在函数内。
2,throws抛出的是异常类,可以抛出多个,用逗号隔开。
   throw抛出的是异常对象。


异常小练习
class FuShuIndexException extends RuntimeException
{
	FuShuIndexException()
	{}

	
	FuShuIndexException(String msg)
	{
		super(msg);
	}
}


class Demo
{
	public int method(int[] arr,int index)//throws FuShuIndexException
	{		

		if(arr==null)
			throw new NullPointerException("数组的引用不能为空!");

		if(index>=arr.length)
		{
			throw new ArrayIndexOutOfBoundsException("数组的角标越界啦,哥们,你是不是疯了?:"+index);
		}
		if(index<0)
		{
			throw new FuShuIndexException("角标变成负数啦!!");
		}
		return arr[index];
	}
}

class  ExceptionDemo3
{
	public static void main(String[] args) //throws FuShuIndexException
	{
		int[] arr = new int[3];
		
		Demo d = new Demo();
		int num = d.method(null,-30);
		System.out.println("num="+num);
		System.out.println("over");
	}
	
}
运行结果


异常处理的捕捉形式:
这是可以对异常进行针对性处理的方式。
具体格式是:
try
{
//需要被检测异常的代码。
}
catch(异常类 变量)//该变量用于接收发生的异常对象
{
//处理异常的代码。
}
finally
{
//一定会被执行的代码。
}
异常处理的原则:
1,函数内容如果抛出需要检测的异常,那么函数上必须要声明。
否则必须在函数内用trycatch捕捉,否则编译失败。
2,如果调用到了声明异常的函数,要么trycatch要么throws,否则编译失败。
3,什么时候catch,什么时候throws 呢?
功能内容可以解决,用catch。解决不了,用throws告诉调用者,由调用者解决 。
4,一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。
内部又几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。

异常的注意事项:
1,子类在覆盖父类方法时,父类的方法如果抛出了异常,
那么子类的方法只能抛出父类的异常或者该异常的子类。
2,如果父类抛出多个异常,那么子类只能抛出父类异常的子集。
简单说:子类覆盖父类只能抛出父类的异常或者子类或者子集。 
注意:如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try .

多线程
进程:正在进行中的程序(直译).
线程:就是进程中一个负责程序执行的控制单元(执行路径)
一个进程中可以多执行路径,称之为多线程。一个进程中至少要有一个线程。
开启多个线程是为了同时运行多部分代码。每一个线程都有自己运行的内容。这个内容可以称为线程要执行的任务。

多线程好处:解决了多部分同时运行的问题。
多线程的弊端:线程太多回到效率的降低。
其实应用程序的执行都是cpu在做着快速的切换完成的。这个切换是随机的。

JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。
1,执行main函数的线程,
该线程的任务代码都定义在main函数中。
2,负责垃圾回收的线程。


创建线程方式一:继承Thread类。
步骤:
1,定义一个类继承Thread类。
2,覆盖Thread类中的run方法。
3,直接创建Thread的子类对象创建线程。
4,调用start方法开启线程并调用线程的任务run方法执行。

创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行。
而运行的指定代码就是这个执行路径的任务。
jvm创建的主线程的任务都定义在了主函数中。

Thread类用于描述线程,线程是需要任务的。所以Thread类也对任务的描述。
这个任务就通过Thread类中的run方法来体现。也就是说,run方法就是封装自定义线程运行任务的函数。
run方法中定义就是线程要运行的任务代码。

开启线程是为了运行指定代码,所以只有继承Thread类,并复写run方法。将运行的代码定义在run方法中即可。 
可以通过Thread的getName获取线程的名称 Thread-编号(从0开始),currentThread获取当前运行的线程对象。
主线程的名字就是main。

创建线程的第二种方式:实现Runnable接口。
1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。
所以要在线程对象创建时就必须明确要运行的任务。
4,调用线程对象的start方法开启线程。

实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务的封装成对象。
2,避免了java单继承的局限性。

所以,创建线程的第二种方式较为常用。

线程安全问题产生的原因:
1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。就会导致线程安全问题的产生。 

解决思路;
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程时不可以参与运算的。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。 

在java中,用同步代码块就可以解决这个问题。
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码 ;
}
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:同步中必须有多个线程并使用同一个锁。

线程间通信:多个线程在处理统一资源,但是任务却不同。
1、创建资源   2、创建任务  3、创建线程执行路径  4、开启线程

等待/唤醒机制
涉及的方法:
1、wait():让线程处于冻结状态,被wait的线程会被存储到线程中。
2、notify():唤醒线程池中一个线程(任务)。
3、notifyAll():唤醒线程池所有线程。

这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法,必须明确到底操作的是哪个锁上的线程。

练习:多生产多消费
//创建资源
class Resource 
{
	private String name;
	private int count = 1;
	private boolean blag = false;

	public synchronized void set( String name)
	{
		while(blag)
			try{ this.wait();}catch (InterruptedException e){}
		this.name = name + count;
		count++;
		System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name);
		blag = true;
		notifyAll();
	}

	public synchronized void out()
	{
		while(!blag)
			try{ this.wait();}catch (InterruptedException e){}
		System.out.println(Thread.currentThread().getName() + ".....消费者....." + this.name);
		blag = false;
		notifyAll();
	}
	
}

//创建生产者
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
			r.set("烤鸭");
	}

}

//创建消费者
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
			r.out();
	}

}
class ProducerConsumerDemo
{
	public static void main(String[] args) 
	{

		Resource r = new Resource();
		Producer p = new Producer(r);	
		Consumer c = new Consumer(r);

		Thread t1 = new Thread(p);
		Thread t2 = new Thread(p);
		Thread t3 = new Thread(c);
		Thread t4 = new Thread(c);

		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}
运行结果


jdk1.5后用Lock代替了synchronized(),可用lock()、unlock()方法。
多了condition对象:await()冻结一个线程、signal()唤醒一个线程、signalAll()唤醒所有线程。

定义锁 Lock lock = new ReentrantLock();
定义监视对象 condition con = lock.newCondition();

Lock接口:出现代替了同步代码块或者同步函数,将同步的隐式锁操作变成现实锁操作,同时更为灵活,可以一个锁上加上多组监视器。
condition接口:出现替代了object中的wait()、notify()、notifyAll()方法,将这些监视器方法单独进行了封装,变成condition监视对象,可以与任意锁进行组合。

wait和sleep方法的区别:
1、wait可以指定时间也可以不指定时间  sleep必须指定时间
2、在同步中对于cpu的执行权和锁的处理不同。
wait释放执行权,释放锁,sleep释放执行权,不释放锁。

停止线程的方式:
1、stop()方法    2、run方法结束
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值