发新话题
打印

JAVA学堂_____从零开始

第三十七课  面向对象的高级特性(抽象类和接口)

在上节课中我们知道了抽象类,可能有人会有这样的问题,有了抽象类了,为什么还要接口呢?比如以前的一个接口的例子,我们用抽象类来表示
public abstract PlayClip{
  void play();
  void loop();
  void stop();
}
public class MyClass extends PlayClip{
  void play(){
  //具体实现
}
void loop(){
  //具体实现
}
void stop();
}
我们说抽象类也是一个类,而类的继承关系只能是单重继承,如果MyClass类继承了一个A类的话,那么它就不能再继承PlayClip类了。
类和派生类之间应该存在一个“is a”的关系,它们在本质上应该是相同的;而接口则不然,并不要求本质的一致性
现在我们举个例子,比如说有个Door的抽象概念,具有两个动作open和close,这时我们分别用abstract和interface来定义:
***********抽象类
abstract class Door{
  abstract void open();
  abstract void close();
}
**********接口
interface Door{
  void open();
  void close();
}
********************这里面我们看都差不多
********************现在我们要增加一个警报方法
***********抽象类
abstract class Door{
  abstract void open();
  abstract void close();
  abstract void alarm();
}
**********接口
interface Door{
  void open();
  void close();
  void alarm();
}
********************那么具有报警功能的AlarmDoor定义方式如下:
class AlarmDoor extends Door{
  void open(){……}
  void close(){……}
  void alarm(){……}
}
或者
class AlarmDoor implements Door{
  void open(){……}
  void close(){……}
  void alarm(){……}
}
这从实现来说没有问题,但它违反了一个原则ISP(接口分离原则),Door的固有方法和报警器的方法混在了一起,那么那些仅仅依赖Door的模块会因为报警器这个概念的改变而改变。所以我们采用了下面的结构:
//抽象类
abstract class Door{
  abstract void open();
  abstract void close();
}
//接口
interface Alarm{
  void alarm();
}
class AlarmDoor implements Door implements Alarm{
  void open(){……}
  void close(){……}
  void alarm(){……}
}

TOP

第三十八课 异常处理(1—基本概念)

JAVA的异常处理机制可以使程序设计人员快速、方便处理程序过程中出现的各种异常情况,在很大程序上提高了程序编写和测试的效率,当异常情况发生时,会创建一个代表该异常的对象,并在产生异常的方法中引发该对象,这个异常最终会被捕获并进行相应处理,异常处理机制为处理具有很多动态运行时特性的复杂程序提供了强大的功能。
在JAVA的异常处理机制中,当程序运行过程中发生错误时,允许其不按照正常路径完成任务,由发现错误的方法抛出封装了错误信息的对象(异常)到其调用程序,发出已经发生问题的信号,然后立即退出;而且,程序并不在调用该方法的代码处继续执行,而是由异常处理机制开始搜索一个能够处理这种特定错误情况的异常处理器。
异常是一个对象,它在程序运行异常时创建,并在发生错误的位置被抛出(throw),由一定的接收机制来接收并处理,这就是异常处理的一个简单过程。这个异常对象必须是某个异常的类的实例,这个异常类必须是已经定义好的,每当程序运行过程中发生一个可识别的错误,即这个错误有一个异常类与之对应,系统就会产生该异常类的一个对象,把当前的进程停止,并抛出异常,然后接收机制接收该异常,并处理以后的事情。
异常的产生是一定有条件的,这个条件表示在出现什么问题的时候应中止程序。我们还必须将异常条件与普通问题区分开,在普通问题的情况下,在当地可能有足够的信息,可在某种程序上解决碰到的问题。而在异常条件的情况下,却无法继续下去,因为当地没有提供解决问题所需的足够多的信息。
在产生异常时,会发生几件事情。第一,按照与创建JAVA对象一样的方法创建异常对象,使用NEW来创建;第二,停止当前执行路径,然后从当前的环境中释放也异常对象的地址。

TOP

第三十九课 异常处理(2——异常分类)
异常处理机制一般来说把代码段形成一个区域,它分为了“警戒区”由try来引导和“异常控制器”由catch引导,结构如下:
try{
   可能产生异常的代码段;
}catch(异常类名1  对象名1){
  处理语句组1;
} catch(异常类名2  对象名2){
  处理语句组2;
}……

try块一般在某个方法的内部,它用来包含有可能抛出异常的代码段,它可以是几个简单的语句,也可以是方法的调用,也可以是很复杂的结构;catch块是专门用来捕获异常的地方。也就是说try中的程序段出现了异常,就要跳转到异常控制器。
其实这个跟多分支是非常相似的,也就是由try产生的异常对象与catch的进行比较,找到对就的catch块去执行其中的语句,只是这个catch不需要break了。
但另外我们也要注意的是,在异常处理中通常需要做一些与系统有关的处理,如释放资源、关闭已打开的数据库、文件等。
我们来看一个例子:
public class ExceptionDemo
{
        public static void main(String args[])
        {
                int i=0;
                int a[]={5,6,7,8};
                for (i=0;i<5;i++)  System.out.println("a["+i+"]="+a);
                System.out.println("3/0="+(3/0));
        }
}
运行后生成的结果为
a[0]=5
a[0]=6
a[0]=7
a[0]=8
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException:4 at ExceptionDemo.main(ExceptionDemo.java:7
那么这个我们知道是下标越界了,在正常输出所有内容后,程序没有结束,这时产生错误,JAVA抛出了一个异常,报告给了java.lang.ArrayIndexOutOfBoundsException,这时程序从异常处结束了运行。如果我们把上面的I<5改为4,我们再看一下结果:
public class ExceptionDemo
{
        public static void main(String args[])
        {
                int i=0;
                int a[]={5,6,7,8};
                for (i=0;i<4;i++)  System.out.println("a["+i+"]="+a);
                System.out.println("3/0="+(3/0));
        }
}
结果为:
a[0]=5
a[0]=6
a[0]=7
a[0]=8
Exception in thread “main” java.lang.ArithmeticException: / by zero at ExceptionDemo.main(ExceptionDemo.java:8
我们看到抛出的异常类型变成了java.lang.ArithmeticException

通过这两个例子我们先简单的理解一下异常,这两个例子我们自己上机试一下吧

TOP

第四十课 异常处理(3)
JAVA中的异常类主要来源于派生自Object类的Throwable类,其结构如下:



Thorwable类是所有异常和错误的父类,所有能够抛出并且能够被控制的异常和错误都必须从这里继承,这的定义格式为:
Public class Throwable extends Object implements Serrializable
   这里我们可以看到Throwable类是Object类的子类,并且实现了接口Serializable
   Thorwable类有public Throwable()和public Throwable(String message)两个构造函数,一个没有参数,一个有参数(可以通过getMessage()得到具体内容)。
基本方法:
  getMessage():用来返回带参数的构造函数创建异常时的参数内容
  toString():返回异常所属的异常类名
  printStackTrace():在输出设备上打印出堆栈使用轨迹
  fillInStackTrace():主要在异常重新抛出时使用
Throwable类派生出Error和Exception两个类
  Error:是不能恢复的严重错误(如资源耗尽)
  Exception:认为是轻微的错误
RuntimeException:编程错误导致的,如被除零等
非RuntimeException:意外情况而发生的,如试图打开错误的URL等

=======================常见异常处理=====================
ArithmeticException        由于除0引起的异常
ArrayStroeException        由于数组存储空间不够引起的异常
ClassCastException                把一个对象归于某个类,但实际上此对象不是该类创建的,也不是该
                        类的子类创建的
IllegalMonitorStateException  监控器状态出错引起的异常
NegativeArraySizeException  数组长度是负数
NullPointerException        程序试途访问一个空数组中的元素或者空对象中的方法或者变量
OutOfMemoryException        使用NEW操作符时,系统无法为其分配内存
SecurityException        访问了不应访问的指地,使用安全性出现问题
IndexOutOfBoundsException  数组下标越界或字符串访问越界
IOException        由于文件未找到、未打开或者I/O操作不能进行
ClassNotFoundException        未找到指定名字的类或者接口引起的
CloneNotSupportException        一个对象引用Object的clone方法,但该对象没有实现cloneable
        接口引起的
InterruptedException        当一个线程处于等待状态,另一个线程试图终止该线程引起的
NoSuchMethodException        所调用的方法没找到引起的
IllegalAcessException        试途访问一个非public方法引起的
StringIndexOutOfBoundsException 访问字符串序号越界
ArrayIndexOutOfBoundsException  访问数组元素下标越界
NumberFormatException        字符的UTF代码数据格式有错引起的异常
IllegalThreadException        线程调用某个方法而所处的状态不适当引起的
FileNotFoundException  为找到指定文件引起的
EOFException        为完成输入操作,由于文件结束引起的

Exception类是异常类的祖先类,所有能捕获的类都是从它派生出来的,我们自己定义的异常类一般也定义成它的子类,当然也同时继承了Throwable类的那些重要方法。可以创建一个控制器,令其捕获所有类型的异常。如:
  Catch(Exception e){
System.out.println(“caught an exception”);
}
这段代码可以捕获任何异常,所以最好将它置于控制器列表的未尾,防止跟随在后面的异常类控制器失效。

有些情况下,这种异常需要人为来抛出,因为这些异常是可以预见发生的,下面我们看一个人为抛出的例子
class JavaThrow{
        public static void main(String args[]){
                try{
                        throw new ArithmeticException();
                }
                catch(ArithmeticException a){
                        System.out.println(a);
                }
                try{
                        throw new ArrayIndexOutOfBoundsException();
                }
                catch(ArrayIndexOutOfBoundsException b){
                        System.out.println(b);
                }
                try{
                        throw new StringIndexOutOfBoundsException();
                }
                catch(StringIndexOutOfBoundsException c){
                        System.out.println(c);
                }
        }
}
在输出的结果中,我们看到:
java.lang. ArithmeticException
java.lang. ArrayIndexOutOfBoundsException
java.lang. StringIndexOutOfBoundsException

TOP

第四十一课 异常处理(3——捕获异常)
try{
   可能产生异常的代码段;
}
catch(异常类名1  对象名1){
  处理语句组1;
}
catch(异常类名2  对象名2){
  处理语句组2;
}
……

这是它的基本结构,我们在第二节的时候已经说过了
现在我们举这样一个例子:
public class ThrowableMethods {
        public static void main(String args[]){
        try{
                throw new Throwable("Here's my Throwable");
}catch(Throwable e){
System.out.println("Caugth Throwable");
System.out.println("e.getmessage():"+e.getMessage());
System.out.println("e.toString():"+e.toString());
System.out.println("e.printStackTrace():");
          e.printStackTrace();
}
}
}
在上面的例子中,抛出的是Throwable类,那么在catch中捕捉的也是Throwable类,否则程序会出现错误。
结果为:
Caught Throwable
e.getMessage():Here’s my Throwable
e.toString():java.lang.Throwable:Here’s my throwable
e.printStackTrace():
java.lang.Throwable:Here’s my Throwable
at ThrowableMethods.main(ThrowableMethods.java:4)

那么如果我们将throw new Throwable("Here's my Throwable");改为:throw new Throwable();得到的结果如下:
Caught Throwable
e.getMessage():Null
e.toString():java.lang.Throwable
e.printStackTrace():
java.lang.Throwable
at ThrowableMethods.main(ThrowableMethods.java:4)

一般来说,捕获的异常应该和抛出的异常类型相同,当然也可以用其父类,但有些时候不能确定抛出的异常类型,可以用Exception类,比如:
import java.io.*;
public class ExceptionExample {
public static void main(String args[]){
try{
throw new IOException("I throw an ArrayIndexOutOfBoundsException");
}catch(Exception e){
System.out.println("Caugth The Exception");
System.out.println("e.getmessage():"+e.getMessage());
}
}
}
输出结果为:
Caught The Exception
e.getMessage(): I throw an ArrayIndexOutOfBoundsException

在JAVA中经常出现的3种异常是:算术异常、字符串越界和数组越界。我们看下面的程序:
public class ThreeException{
public static void main(String args[]){
char c;
int a,b=0;
int Array []=new int [7];
String s="Hello";
try{
a=1/b;
}catch(ArithmeticException ae)
{System.out.println("Catch "+ae);
}
try{
Array[8]=0;
}catch(ArrayIndexOutOfBoundsException ai){
System.out.println("Cat "+ai);
}
try{
c=s.charAt(8);
}
catch(StringIndexOutOfBoundsException se){
System.out.println("catch "+se);
}
}
}
那么运行结果为:
Catch java.lang.ArithmeticException: / by zero
Catch java.lang.ArrayIndexOutOfBoundsException:8
Catch java.lang.StringIndexOutOfBoundsException:String index out of range:8

TOP

第四十二课  异常处理(4—重新抛出异常)

上面我们讲到的异常都是由程序直接产生的,现在我们看一个由方法产生的异常的例子:
public class ExceptionDemo2
{
        public static void main(String args[]){
                try{
                        CramerDemo equation=new CramerDemo();
                        int [] solution=new int[2];
                        int a[]={1,2},b[]={1,-1},k[]={3,6};
                        solution=equation.cramer(a,b,k);
                        System.out.println("x="+solution[0]+"y="+solution[1]);
                        b[1]=2;
                        solution=equation.cramer(a,b,k);
                        System.out.println("x="+solution[0]+"y="+solution[1]);
                        int[] newb={1};
                        solution=equation.cramer(a,newb,k);
                        System.out.println("x="+solution[0]+"y="+solution[1]);
                }
                catch(IndexOutOfBoundsException e)
                {e.printStackTrace();
                }
        }
}
class CramerDemo
{
        public int [] cramer(int a[],int b[],int k[])
        throws IndexOutOfBoundsException
        {
                int [] solution=new int [2];
                try
                {
                        int delta,deltax,deltay;
                        delta=a[0]*b[1]-a[1]*b[0];
                        deltax=k[0]*b[1]-k[1]*b[0];
                        deltay=k[0]*a[1]-k[1]*a[0];
                        solution[0]=deltax/delta;
                        solution[1]=deltay/delta;
                }
                catch(ArithmeticException e)
                {
                        System.out.println("None or infinite number of solutions.");
                }
                return solution;
        }
}
程序的输出结果如下:
x=3 y=0
None or infinite number of solutions.
X=0 y=0
Java.lang.ArrayIndexOutOfBoundsException:1
At CramerDemo.cramer(ExceptionDemo2.java:39)
At ExceptionDemo2.main(ExceptionDemo2.java:19)

我们可以看到由printStackTrace将程序调用的堆栈打印出来后,可以了解到异常最行发生在
At CramerDemo.cramer(ExceptionDemo2.java:39)
也就是CramerDemo中,然后接下来发生在
At ExceptionDemo2.main(ExceptionDemo2.java:19)
也就是在main()中
这样我们可以很方便的知道程序的错误到底产生在哪
  在某些情况下,,需要重新抛出刚才产生过的异常,特别是在用Exception捕获所有可能的异常时,由于已拥有当前的异常的对象名,所以只需要简单抛出那个对象名即可,我们还是通过一个例子来分析:
public class RethrowException{
        public static void f() throws Exception
        {
                System.out.println("the original exception in f()");
                throw new Exception("thrown from f()");
        }
        public static void main(String args[]) throws Exception
        {
                try
                {
                f();}
                catch(Exception e)
                {
                        System.out.println("Caught in main");
                        throw e;
                }
        }
}
输出结果为:
the original exception in f()
   caught in main
Exception in thread “main” java.lang.Exception:throw from f()
   At RethrowException.f(RethrowException.java:5)
   At RethrowException.main(RethrowException.java:11)
重新抛出的异常将进入上一级结构的异常控制器中,但在这一层次的异常控制机制中,后面的catch仍然被忽略。这个被再次抛出的异常保留原异常的有关信息,所以用于捕获这个再抛出异常的上一层控制器可以从那个对象里提取也所有信息。
在重新抛出异常时,用printStackTrace()打印出被重新抛出的异常的堆栈跟踪信息,它将与异常的起源地对应,而不是与重新抛出它的位置对应。若想显示重新抛出异常的位置,可调用fillInStackTrace(),它的返回值是一个特殊的异常对象,这个对象代替被重新抛出的异常,因此,它含有的信息就只有重新抛出点的信息,而覆盖了原来对象的信息,我们来看下面的例子:
public class Rethrowing{
        public static void f() throws Exception
        {
                System.out.println("originating the exception in f()");
                throw new Exception("thrown from f()");
        }
        public static void g() throws Throwable
        {
                try
                {
                        f();
                }
                catch(Exception e)
                {
                        System.out.println("Inside g(),e.printStackTrace():");
                        e.printStackTrace();
                        throw e;
                }
        }
        public static void main(String args[]) throws Throwable
        {
                try{
                        g();
                }
                catch(Exception e)
                {
                        System.out.println("Caught in main,e.printStackTrace():");
                        e.printStackTrace();
                }
        }
}
这个是我们用printStackTrace()得到的结果,结果如下:
origiting the exception in f()
Inside g(), e.printStackTrace();
Java.lang.Exception:thrown from f()
      At Rethrowing.f(Rethrowing.java:5)
      At Rethrowing.g(Rethrowing.java:11)
  At Rethrowing.main(Rethrowing.java:24)
Caught in main,e.printStackTrace():
   At Rethrowing.(Rethrowing.java:5)
   At Rethrowing.g(Rethrowing.java:11)
   At rethrowing.main(Rethrowing.java:24)
如果我们把第十七行中的throw e替换为throw e.fillInStackTrace();结果就不一样了,这个大家可以换一下
origiting the exception in f()
Inside g(), e.printStackTrace();
Java.lang.Exception:thrown from f()
      At Rethrowing.f(Rethrowing.java:5)
      At Rethrowing.g(Rethrowing.java:11)
  At Rethrowing.main(Rethrowing.java:24)
Caught in main,e.printStackTrace():
   At Rethrowing.g(Rethrowing.java:11)
   At rethrowing.main(Rethrowing.java:24)
那么它的意思是“抛出异常,把它当新对象,它的信息填充原来对象的信息

TOP

第四十三课  异常处理(5—Finally关键字)

  我们要使用finally的作用是只要不管try块中是否抛出了异常,不管异常是否被捕获,由哪个控制器捕获,在结束当前的异常控制结构之前,系统会去finally从句,这样,每次都需要执行的代码可以放入finally从句,可以让程序较为简洁,一般来说它要放在异常控制器的末尾。
try{
   可能产生异常的代码段;
}
catch(异常类名1  对象名1){
  处理语句组1;
}
catch(异常类名2  对象名2){
  处理语句组2;
}
finally{
  总会执行的语句
}

finally在文件处理时非常有用
try{
  对文件进行处理的程序
}
catch(IOException e)
{
  对文件异常进行处理
}
finally{
  不论是否发生异常,都关闭文件
}
看下面的例子:
public class FinallyWorks
{
static int count=0;  //设置一个静态计数器
public static void main(String args[])
{
while(true)
{
try
{
System.out.print("count="+count+":");
if(count++%2==0) throw new Exception();
System.out.println("Noexception");
}catch(Exception e){
System.out.println("Exception thrown");
}
finally{
System.out.println("finally clause executed");
if(count>=5)break;  //用于跳出无限循环
}
}
}
}
结果为:
count=0:Exception thrown
finally clause executed
count=1:Noexception
finally clause executed
count=2:Exception thrown
finally clause executed
count=3:Noexception
finally clause executed
count=4:Exception thrown
finally clause executed
这个程序在count为偶数时抛出Exception类异常,但不管是否抛出异常,都要去执行finall子句

TOP

第四十五课  JAVA的输入与输入(1——字节流)
JAVA系统将计算机对外部设备的输入/输出、文件读/写、网络的读/写都归纳为输入/输出操作来处理,同时将线程之间的数据通信也归为输入/输出操作。
JAVA使用流(stream)来执行输入/输出功能,用于读入数据则称为输入流,若用于写出数据则称为输出流。在JAVA中提供了字节流(byte)及字符流(character),这些流定义在java.io包中,它专门为程序提供I/O操作的各种类。
InputStream和OutputStream为字节流的输入和输出类,是所有面向字节的输入/输出流父类;Reader和Writer为字符流的输入和输出类,是所有面向字符的输/输出流的父类;File和RandomAccessFile为对文件操作的类,File记录文件信息以顺序方式访问,RandomAccessFile类以随机方式访问文件。
下面我们首先讲一下字节流:
字节流用来读写8位数据,由于不会对数据做任何转换,因此可用来处理二进制数据。
我们先来看一个例子:
import java.io.*;
public class ReadKeyboard{
public static void main(String args[]) throws IOException
{
int a;
System.out.print("请输入一个字符:");
a=(char)System.in.read();
System.out.println("你输入的字符是:"+a);
}
}
说明:
1、        read()方法被定义为抽象方法,主要是为了让继承InputStream类的子类可以针对不同的外部设备定义不同的read()方法
2、        JAVA规定read()必须配合异常处理机制来使用。
3、        InputStream是抽象类,不能实例化,因此在实际应用中并不使用这个,类而是使用其子类。

我们再来看一个例子:
import java.io.*;
public class OpenFile{
public static void main(String args[]){
try{
FileInputStream fis=new FileInputStream("OpenFile.java");
int n=512;
byte buffer[]=new byte[n];
while((fis.read(buffer,0,n)!=-1)&&(n>0)){
System.out.print(new String(buffer));
}
System.out.println();
fis.close();
}
catch(IOException ioe){
System.out.println(ioe);
}
catch(Exception e){
System.out.println(e);
}
}
}

TOP

第四十六课  JAVA的输入与输入(1——字符流A)

  字符流主要是用于支持Unicode的文字内容,绝大多数在字节流所提供的类,可在此找到对应的类。其中输入流Reader抽象类帮助用户在Unicode流内获得字符数据;而Writer类则是实现了输出。可以利用Reader类来读取由Writer写入的流
我们来看一个例子:
import java.io.*;
public class ReadLine{
public static void main(String args[]) throws IOException{
BufferedReader in =new BufferedReader(new InputStreamReader(System.in));
String a;
System.out.print("请输入任一字符串:");
a=in.readLine();
System.out.println("你输入的字符串是:"+a);
in.close();
}
}
我们说Reader类是抽象的,不能实例化,于是我们用BufferedReader类建立了实体,当然这是个简单的例子,实现方式不止一种,比如前面我们介绍了FileInputStream的用法,与之对应的是FileReader类,我们来看一个例子
import java.io.*;
public class FileReaderDemo{
public static void main(String args[]){
try{
final int n=256;
char buffer[]=new char[n];
FileReader fr=new FileReader("write.txt");
int readNum=fr.read(buffer);
String str=new String(buffer,0,readNum);
System.out.println("readfile data:"+str);
fr.close();
}
catch(IOException ioe){
System.out.println(ioe);
}
}
}

PS:在运行程序前需建立write.txt文件

TOP

第四十七课  JAVA的输入与输入(2——字符流B)

Write类为字符输出的抽象基础类,同Rearder类一样,必须用Writer类的子类将字符信息写入到流内。比如,可以利用StringWriter类或CharArrayWriter类来将字符写入字符串或数组的缓冲区内,或者利用OutputStreamWriter类连接字符与字符流。
我们来看一个例子
import java.io.*;
public class CopyString{
public static void main(String args[]){
        String str="This is IO String!!";
        //用于测试的字符串
        boolean boo=true;
        StringWriter out=new StringWriter();
        System.out.println("原字符串流:"+str);
  try
        {
                out.write(str,5,5);
                //写入流
                //读出字符
                StringReader in=new StringReader(out.toString());
                System.out.println("输出字符串流"+out.toString());
                //输出out流内容
                int con=in.read();
                //读出第一个字符
                if (con==-1) boo=false;
                while(boo){
                        System.out.print((char)(con));
                        con=in.read();
                        if(con==-1)
                          boo=false;  //是否到流最后
                        }
                }
                catch(IOException e)
                {
                        System.out.println(e);
                }
        }
}

TOP

发新话题