成鹏致远 |
lcw.cnblog.com
|2014-01-28
异常
1.异常的基本概念
- 异常是导致程序中断运行的一种指令流
- 基本的异常处理格式:try...catch,try中捕获异常,出现异常之后的代码将不再被执行,而是中转到相应的catch语句中执行,用于处理异常
- 对于异常也可以设置其统一的出口,使用finally完成
- 在整个JAVA的异常结构中,实际上有两个最常用的类:Exception、Error,这两个类全都是Throwable的子类
- Exception:一般表示的是程序中出现的问题,可以直接使用try...catch处理
- Error:一般指的是JVM错误,程序中无法处理
- 【注意】一般在输出异常信息的时候,可以直接使用System.out.println()打印异常对象,也可以通过Exception提供的一个方法:public void printStackTrace()
- Java的异常处理机制处理步骤
- 一旦产生异常,则首先会产生一个异常类的实例化对象
- 在try语句中对此异常对象进行捕捉
- 产生的异常对象与catch语句中的各个异常类型进行匹配,如果匹配成功,则执行catch语句中的代码
- 如果有一些其它的异常无法知道的话,可以最后使用Exception进行捕获
- 【注意】在异常处理中,捕获更粗的异常要放在捕获更细的异常之后
- 当所有的异常处理的方式是一样的就可以使用直接对Exception进行捕获,当然,在一个比较细致的开发中是不建议这样使用的,所有的异常最好分别捕获
- 小结
- 异常出现之后,如果没有合理的处理的话,则会让整个程序中断执行
- 使用try...catch和try...catch...finally可以处理异常,finally将作为异常的统一出口,不管是否有异常都会执行此语句
- 一个异常处理中可以同时出现多个catch,但是捕获更粗的异常要放在捕获更细的异常之后,否则程序编译的时候将出现错误
- 在异常中最大的类是Throwable,分为两个子类:Exception、Error
- Exception:是程序可以自己处理的异常
- Error:表示JVM错误,一般程序无法处理
- 捕获的时候可以直接捕获Exception,但是最好分开捕获,如果所有的异常处理操作是一样的话,则也可以直接捕获Exception
- 每当异常产生之后,会在程序中产生一个异常类的实例化对象,之后使用此对象与catch中的异常类型相匹配,如果匹配成功,则执行catch语句中的内容,如果匹配不成功,则向下继续匹配,如果都无法成功,程序中出现中断执行的问题
2.异常中的其它概念
- 在定义一个方法的时候可以使用throws关键字声明,使用throws声明的方法表示此方法不处理异常,而交给方法处进行处理
- JAVA中最大的头就是JVM,所以如果在主方法中使用了throws关键字,则表示一切的异常交给JVM进行处理,默认的处理方法也是使用JVM完成的
- throw可以直接抛出一个异常,抛出的时候直接抛出异常类的实例化对象即可
- 在Java的异常处理机制中
- 如果抛出的是Exception的,则必须使用try...catch进行处理
- 如果抛出的是RuntimeException的类型,则不是必须使用try...catch处理,一旦发生异常之后将由JVM进行处理,但是为了保证程序的健康性,建议在有可能出现异常的地方还是老实的使用try...catch进行处理
- 自定义异常类只需要继承Exception不可以了
- 在JDK 1.4之后,系统增加了断言的功能,就是断定某一个操作的结果肯定是正确的,如果程序执行到出现断言语句的时候发现结果不正确了,则会出现错误的信息
- 断言就是肯定某一个结果的返回值是正确的,如果最终结果的返回值是错误的,则通过断言检查会为用户提示错误信息
- 断言本身不会影响程序的执行,但是如果要想让一个断言起作用,则必须对断言进行验证
- 小结
- 断言在实际开发的使用中并不是很常见
- throw和throws关键字联合使用问题
- throw:抛出异常对象
- throws:在方法声明处使用,表示此方法不处理异常
- Exception的异常是必须处理的,而RuntimeException的异常是可以不处理的,但是为了保证程序的运行,只要有异常是好全部处理
- 如果需要自定义异常,则直接继承Exception类即可
包及访问控制权限
1.包的定义及导入
- 多人开发:在JAVA中,可以将一个大型项目中的类分别独立出来,分门别类地存到文件里,再将这些文件一起编译执行,如此的程序代码将更易于维护
- package是在使用多个类或接口时,为了避免名称重复而采用的一种措施,直接在程序中加入package关键字即可
- 定义包之后,实际上类的名称就是:包.类名称
- import语法
- import 包名称.子包名称.类名称:手工导入所需要的类
- import 包名称.子包名称.*:由JVM自动加载所需要的类
- 实际上来说这两种性能都是一样的,因为如果使用后者,是由JVM帮助用户决定需要导入的类,不需要的类,是不会被加载进来的
- 如果一个类只在本包中访问,不需要被外包访问,则直接声明成class即可,而如果一个类需要被外包访问,则必须声明为public class
- JAVA新特性:静态导入
- 在JDK 1.5之后提供了静态导入功能
- 如果一个类中的方法全部是使用static声明的静态方法,则在导入的时候就可以直接使用"import static"的方式导入
- 导入格式:import static 包.类.*;
- JAR命令中的主要参数
- “C”:创建新的文档
- “V”:生成详细的输出信息
- “F”:指定存档的文件名
- 在实际的Java开发中往往把一些实用的工具类打成jar包交给用户使用
- 小结
- 包可以将很多的class文件分类的存放好,这样可以避免多人开发时,类文件重名的情况
- 在实际的开发中,没有包的类基本上不存在的,完整的类名称“包.类名称”
- 如果导入了不同包的同名类的时候,可以直接通过完整的包.类名称避免重复
- JDK 1.5之后提供了静态导入功能,可以直接使用一个类的静态方法
- 如果一个包中的全部类要交付用户使用,则要将其打成一个jar包
2.访问控制权限入命名规范
- 访问控制权限
- private:可以定义方法,属性,定义的方法和属性不能被类的外部所看到
- default:可以在本包中的任意地方访问
- protected:保护,不同包的非子类不可以访问
- public:公共的,都可以访问,不受任何的限制
- 回顾
- 当产生了一个类之后,为了保证类中的内容不被外部直接看到,则使用pribate关键字
- 但是,如果现在两个有关系的类要进行属性互相访问的话就比较麻烦,之前只能使用getter/setter取得设置,所以为了减少私有属性的访问的麻烦,使用了内部类,但是内部类本身会破坏程序的结构
- 为了让一个类的内容可以断续方便的使用,使用了继承的概念,但是在继承中private属性也是无法被子类看到的,所以此时,为了方便子类的操作,可以将属性使用protected进行封装,这样一来外部也无法直接看到(不同包)
- 之后有了继承之后,既然有了父子的关系,所以就可以使用向上或向下的转型操作,以完成多态性,但是在开发中类与类之间的直接继承并不多见,而往往继承抽象类或实现接口,当若干个操作间需要解耦合的时候就可以使用接口完成
- 既然有内部类,则如果一个接口或抽象类的子类只使用一次,则可以将其定义成匿名内部类
- 开发中没有包的类是绝对不存在的
- 封装->继承->多态
多线程
1.认识多线程
- 在JAVA中如果要想实现多线程可以采用两种方式
- 继承Thread类
- 实现Runnable接口
- 一个类继承了Thread类之后,那么此类就具备了多线程的操作功能
- 【问题】为什么不直接调用 run()方法,而是通过 start()调用呢
- 查阅JAVA源代码
- start()方法有可能抛出异常
- stopBeforeStart是一个boolean类型的变量
- native关键字表示的是一个由Java调用本机操作系统函数的一个关键字。
- 在Java中,运行JAVA程序调用本机的操作系统的函数以完成特定的功能
- 证明:如果现在要想实现多线程的话,则肯定需要操作系统的支持,因为多线程操作中牵扯到一个抢占CPU的情况,要等待CPU进行高度,那么这一点肯定需要操作系统的底层支持,所以使用了 native调用本机的系统函数,而且在各个操作系统中多线程的实现底层代码肯定是不同的,所以使用 native关键字也可以让 JVM自动去调整不同的 JVM实现
- threadStatus也表示一种状态,如果线程已经启动了再调用 start()方法的时候就有可能产生异常
- Thread类与Runnable接口的联系
- Thread类也是Runnable接口的子类
- 从Thread类与Runnable类的联系上来看,做法非常类似于代理设计模式,Thread类完成比线程主体更多的操作,例如:分配CPU资源,判断是否已经启动等等
- 使用Thread类在操作多线程的时候无法达到资源共享的目的,而使用Runnable接口实现的多线程操作可以实现资源共享
- Thread类与Runnable接口的使用结论
- 实现Runnable接口比继承Thread类有如下的明显优点
- 适合多个相同程序代码的线程去处理同一个资源
- 可以避免由于单继承局限所带来的影响
- 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的
- 综合以上来看,开发中使用Runnable接口是最合适的
- 实现Runnable接口比继承Thread类有如下的明显优点
- 线程的状态
- 多线程在操作中也是有一个固定的操作状态的
- 创建状态:准备好了一个多线程的对象:Thread t = new Thread()
- 就绪状态:调用了 start()方法,等待 CPU进行高度
- 运行状态:执行 run()方法
- 阻塞状态:暂停止执行,可能将资源交给其他线程使用
- 终止状态(死亡状态):线程执行完毕了,不再使用了
- 多线程在操作中也是有一个固定的操作状态的
- 实际上,线程调用 start()方法的时候不是立刻启动的,而是等待 CPU进行高度
- 小结
- 进程与结进程的区别,关系
- 线程是在进程的基础之上划分的
- 线程消失了进程不会消失,进程如果消失了,则线程肯定会消失
- Java进程实现的两种方式
- 继承Thread类
- 实现Runnable接口
- 线程的启动:通过 start()方法完成,需要进行 CPU高度,调用 start()实际上调用的就是 run()方法
- Thread类也是Runnable的子类,使用了代理的机制完成
- 在使用多线程的实现中建议通过Runnable接口实现,这样可以避免由于单继承所带来的开发局限,而且通过使用Runnable接口也可以达到资源共享的目的
- 线程的状态
- 进程与结进程的区别,关系
2.线程常用操作方法
- 在多线程中所有的操作方法实际上都是从Thread类开始的,所有的操作基本上都在Thread类之中
- 取得当前结进程:通过 currentThread()方法取得当前正在运行的进程对象
- 【问题】主方法是以线程的形式出现的,那么JAVA运行时到底启动了多少呢
- 每当java程序执行的时候,实际上都会启动一个JVM,每一个JVM实际上就是在操作系统中启动了一个进程,Java中本身具备了垃圾收集机制,所以java运行时至少启动两个线程:主线程、GC
- 判断线程是否启动:isAlive()
- 线程的强制运行:在线程操作中,可以使用join()方法让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等此线程完成之后才可以继续执行
- 线程休眠:在程序中允许一个线程进行暂的休眠,直接使用 Thread.sleep()方法即可
- 线程的中断:一个线程可以被另外一个线程中断其操作的状态,使用 interrupt()方法完成
- 线程的礼让:在线程操作中,也可以使用 yield()方法将一个线程的操作暂让给其他线程执行
3.同步与死锁
- 所谓的同步就是指多个操作在同一个时间段内只能有一个线程进行,其他线程要等待此线程完成之后才可以继续执行
- 要想解决资源共享的同步操作问题,可以使用同步代码块及同步方法两种方式完成
- 【回顾】代码块分为四种
- 普通代码块:是直接定义在方法之中的
- 构造块:是直接定义在类中的,优先于构造方法执行,重复调用
- 静态块:是使用 static关键字声明的,优先于构造块执行,只执行一次
- 同步代码块:使用 synchronized关键字声明的代码块,称为同步代码块
- 写同步代码块的时候,必须指明同步的对象,一般情况下会将当前对象作为同步的对象,使用this表示,格式:synchronized(同步对象){需要同步的代码;}
- 同步方法:synchronized 方法返回值 方法名称(参数列表){}
- 死锁:同步可以保证资源共享操作的正确性,但是过多同步也会产生问题,死锁一般情况下就是表示在互相等待
- Object类对线程的支持
- 等待:wait()方法
- 唤醒:notify()、notifyAll()方法
- 小结
- 多个线程在访问同一资源的时候需要进行同步操作
- 同步使用 synchronized关键字完成,分为同步代码块及同步方法
- 过多的同步有可能造成死锁的产生,死锁是在程序运行时的一种表现状态
4.线程的生命周期
- 一个新的线程创建之后通过 start()方法进入到运行状态,在运行状态中可以使用 yield()进行礼让,但是仍然可以进行
- 如果现在一个线程需要暂停的话,可以使用 suspend()、sleep()、wait()
- 如果现在线程不需要再执行,则可以通过 stop结束(如果 run()方法执行完毕也表示结束),或者一个新的线程直接调用 stop()方法也可以进行结束
- 以上有如下的几个方法
- suspend():暂时挂起线程
- resume():恢复挂起的线程
- stop():停止线程
- 因为以上三个方法都会产生死锁的问题,所以现在已经不建议使用了
- 如果现在要想停止一个线程的运行,可以通过设置标志位,让线程停止运行
泛型
1.泛型入门
- 泛型是在 JDK 1.5之后增加的新功能,泛型(Generic)
- 泛型可以解决数据类型的安全性问题,它主要的原理,是在类声明的时候通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化的时候只要指定好需要的类型即可
- 泛型也可以在构造方法中使用,一般有可能使用构造方法为类中的属性赋值
- 构造方法可以为类中的属性初始化,那么如果类中的属性通过泛型指定,而又需要通过构造设置属性内容的时候,那么构造方法的定义与之前并无不同,不需要像声明类那样指定泛型
- 在泛型中也可以同时指定多个泛型类型
- 在泛型应用中最好在声明类对象的时候指定好其内部的数据类型
- 如果在泛型类实例化时没有指定泛型的类型,则在java中为了保证程序依然可以使用,会将 T设置成 Object类型,这样一来,就可以接收任意的数据类型,也就是说此时的类型就是 Object,所有的泛型信息将被擦除
- 小结
- 泛型的产生意义:为了保证数据的安全性
- 泛型的基本使用:由外部指定其具体的操作类型
2.通配符
- 匹配任意类型的通配符:在开发中对象的引用传递是最常见的,但是如果在泛型类的操作中,在进行引用传递的时候泛型类型必须匹配才可以传递,否则是无法传递的
- 在使用 <?>时只能接收,但是不能修改
- 受限泛型
- 在引用传递中,泛型操作中也可以设置一个泛型对象的范围上限和范围下限
- 范围上限使用 extends关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类
- 范围下限使用 super进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直到 Object类
- 【解释】泛型与子类继承的限制:一个类的子类可以通过对象多态性,为其父类实例化,但在泛型操作中,子类的泛型类型是无法使用父类的泛型类型接收的,例如:Info<String>不能使用 Info<Object>接收
- 小结
- 使用 ?可以接收任意的泛型对象
- 泛型的上限:? extends 类型
- 泛型的下限相对理解一些就可以了
- 了解为什么泛型子类之间的继承无法直接转换的原因
3.泛型的其它应用
- 定义泛型接口:在 JDK1.5之后,不仅仅可以声明泛型类,也可以声明泛型接口,声明泛型接口和声明泛型类的语法类似,也是在接口名称后面加上 <T>,格式:[访问权限]interface 接口名称<泛型标识>{}
- 如果现在实现接口的子类不想使用泛型声明,则在实现接口的时候直接指定好其具体的操作类型即可
- 泛型除了可以为类中的属性指定类型之外,也可以定义方法,泛型方法所在的类中是否是泛型本身是没有任何关系的
- 定义泛型方法
- 泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型
- 泛型方法的简单定义:[访问权限]<泛型标识>泛型标识 方法名称([泛型标识 参数名称])
- 使用泛型方法的时候,也可以传递或返回一个泛型数组
- 在设置泛型的时候也可以嵌套的设置
- 小结
- 泛型在接口上可以定义,及其实现的方式
- 泛型在使用的时候可以进行嵌套的操作,只要根据其操作语法即可
- 泛型方法上使用泛型标记的时候需要先声明,同样可以指定其操作的上限和下限