黑马程序员——Java基础——内部类、异常、包

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------


一、内部类

   1定义

       将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)。

编译时,如果代码中有内部类,生成的class文件中会含有这样的文件:Test$1.class。编译器将会把内部类翻译成用$(美元符号)分隔外部类名和内部类名的常规类文件。这是内部类的一种编译现象。

   2、内部类的访问特点

       1) 内部类可以直接访问外部类中的成员,包括私有成员。

       2) 外部类要访问内部类中的成员必须要建立内部类的对象。

  3、访问格式

    A当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中(比如main所在类)可以直接建立内部类对象。

        格式:

            外部类名.内部类名  变量名 =外部类对象.内部类对象;

        如:    Outer.Inner in =new Outer().new Inner();

B当内部类在外部类中的成员位置上时,可以被成员修饰符所修饰。

比如private:将内部类在外部类中进行封装。 

        static:内部类就具备static的特性。但是当内部类被static修饰后,只能直接访问外部类中的static成员。

在外部其他类中,直接访问static内部类的非静态成员的格式为:

        new 外部类名.内部类名().方法名();

        如:new Outer.Inner().function();但是这种方式不常用

在外部其他类中,直接访问static内部类的静态成员格式为:

        外部类名.内部类名.方法名();

        如:Outer.Inner.function();

  C 在内部类中不能有静态声明,这也是内部类的一个特点。

      1)当内部类中定义了静态成员时,该内部类必须是static的。

      2)当外部类中的静态方法访问内部类时,内部类也必须是static的。

      3)在实际应用中,内部类通常被定义为private,而很少定义为public。通常被定义为public的是接口

  D  内部类定义在外部类的方法内

        此时,创建了这个类型的对象时,仅仅使用了一次,那么可在这个方法中定义内部类,这又称为局部类。此时,它的访问规则没有变,还是可以直接访问外部类变量,省略了Out.this.

    局部内部类的特点

        1)不可以被成员修饰符修饰。如publicprivatestatic等修饰符修饰,因为修饰符只能修饰成员,它的作用域被限定在了声明这个局部内部类的代码块中

        2)可以直接访问外部类中的成员,因为还持有外部类中的引用。

        3)内部类不可以访问它所在方法内的局部变量,只能访问被final修饰的局部变量。

程序:

 

 4、匿名内部类

要点一,匿名内部类其实就是内部类的简写格式。

要点二,定义匿名内部类的前提。内部类必须是继承一个类或者实现接口。一个内部类可以直接继承一个外部类。

abstract   class  AbsDemo

{

  abstract void show();

}

class  inner   extends   AbsDemo

{

  Void show()

{

System.out.println(show:+x);}

}

匿名内部类的形式来写这个程序:

new AbsDemo()

{

  void show()

   {

   System.out.println(x=+x);

   }

}.show();

要写匿名内部类,就要明白内部类所做的事情:a.继承一个外部类;b.将外部类的函数进行复写c.创建了一个内部类对象d.调用show方法。

这段代码表达的就是一个AbsDemo的子类对象,并调用它所复写父类的show方法。

要点三,匿名内部类的格式

new 父类或者接口()//也可以传参数

{定义子类的内容}

要点四,其实匿名内部类就是一个匿名子类对象,只是这个对象有点胖。可以理解为带内容的对象匿名内部类在实际开发中很常见。

要点五,匿名内部类中复写父类的方法不要超过3个,会影响代码阅读性。

   如果想要调用匿名内部类中的两个或以上的方法,如何做呢?

 不能够再写一个匿名内部类,那就是两个对象了。

可以给匿名内部类起个名字,匿名内部类没有名字,但是其父类可以有名字,可以利用多态的特性,用父类的引用来指向子类对象,从而调用子类方法。

AbsDemo d=new AbsDemo()

{

   void show()

{

  System.out.println(x=+x);

}

void  method()

{

   System.out.println(method() ..... run );}

};

D.show(); 

D.method();

但前提是method方法也必须在父类中有定义,否则依照多态的规则,父类引用是不能访问父类没有而子类特有的方法。

所以匿名内部类中,一般不写子类特有的方法,没有意义,因为不能被调用。

练习

Interface inter

{

  Void  method();

}

Class Test

{

  //补足代码 通过匿名内部类

}

 Main函数中:

Test.function().method();

程序:

 

二、异常

   1、概述

     如果程序在编译时没有错误信息产生,而程序运行时出现一些运行时的错误,这样的错误就是被称为异常。为了对这样的异常进行处理,Java提供了异常处理机制。

   秉承java万物皆对象的思想,同样把所有的异常封装成类,除了内置的异常类之外,Java也可以自定义异常类。此外,Java的异常处理机制也允许抛出自定义异常。

而在程序设计中,必须考虑到可能发生的异常事件,并做出相应的处理,才能保证程序可以正常运行。

例如:

class Demo

{

  int div(int a,int b)

{

  return a/b;
    }

}

public static void main(String[]args){

Demo d=new Demo();

int x=d.div(4,1);

System.out.println(x=+x);

 }

程序编译正常,但当我们给函数div中传入的是40,就会出现除0异常,也就是依照数学规则,除数不能为0.这就是我们所说的异常。

其实就是java对不正常情况进行描述后的对象体现。

一个问题,里面有问题的由来,问题的信息,问题的结果。把这些封装成对象以后,都可以通过对象内部的功能进行操作。

2、异常的体系划分

对于问题的划分:分为两种:一种是严重的问题,一种是非严重的问题。

对于严重的,java通过Error类进行描述,对于非严重的,java语言通过Exception类进行描述。

比如生活中的疾病,可以封装成对象,疾病可以分为两类,可治愈和不可治愈。可治愈就是Exception,不可治愈就是Error

     1)异常体系Throwable

        无论error 或者exception都具有一些共性内容。比如:不正常情况的信息,引发原因等。

有了共性内容,我们知道,可以向上抽取,形成父类,叫做Throwable

   Throwable

       |-----Error

       |------Exception

Errorexception下面都有很多子类,异常体系中的类有一个特点,都是以父类为后缀的。比如

RuntimeexceptionIOexception等等。

  异常体系的特点:  异常体系中的所有类以及建立的对象都具备可抛性,也就是说可以被throwthrows关键字所操作。只有异常体系具备这个特点。

 2)Error

   通常指出现重大问题如:运行的类不存在或内存溢出等。这是虚拟机告诉你的。因为超出虚拟机处理范围了。JavaError类描述Java运行系统中的内部错误及资源耗尽的错误。Error类表示问题很严重,仅靠修改程序代码是不能修复,也被成为致命类。

例如:byte[]arr=new byte[1024*1024*1024];

    内存溢出   OutOfMemoryError

对于error,通常不编写针对代码对其处理。发生error时,应该建议终止程序。

 3Exception

 发生这种异常时,表示通过处理,可以保证程序正常运行,也称为非致命类。对于Exception可以使用针对性的处理方式进行处理,Java通过Exception类对该异常进行描述。

 

3、Exception异常的分类

   Exception类又根据错误发生的原因分为两种类型:

   1)编译时被检测异常

        也就是该类型的异常,在程序中如果没有处理,捕获或者抛出,编译会失败。       

   2)运行时异常(不被检测异常)

       即RuntimeException异常。是程序员编写的错误程序导致的,修改错误,就可以继续执行。

在程序中引发该异常的情况有:除数为0的运算、数组角标越界、对没有引用对象的变量进行操作等。

当发生RuntimeException类型的异常后,可以不通过try-catch语句、throws语句捕获或抛出在编译可以通过,只是在运行时由java虚拟机抛出。

4、异常的处理 

处理方式一  抛出

 使用关键字throws或者throw

 两者的区别是:

  1)  throw定义在函数内,用于抛出异常对象。

  2)  throws定义在函数上,声明抛出异常类,可以声明抛出多个异常,它们用逗号隔开。

 当函数内容有throw抛出异常对象,并未进行try处理。必须要在函数上声明,否则编译失败。

注意:函数内如果抛出的RuntimeExcpetion异常,函数上可以不用声明。

处理方式二  捕获处理

  java语言的异常处理器由trycatchfinally3个语句块构成。

try

{

  需要被检测的代码;

}

catch(异常类  变量)

{

  处理异常的代码;或者说是处理方式

}

finally

{

  一定会执行的语句;

}

其中,try块用于存放可能发生异常的正常的java代码。

  catch块放在try块之后,用于激发被捕获的异常并处理异常。

  finally块是异常处理结构的最后执行部分,不论try块中代码如何退出,都将执行finally块。通常finally块中都存放的是释放资源语句。

注意:如果程序中有退出语句:system.exit(0);那么finally块将不被执行。

语句格式有三种:

 第一种:try——Catch

try

{

}

catch(  )

{

}

第二种:try——catch——finally

try

{

 

}

catch()

{

 

}

finally

{

}

第三种:try——finally

try

{

}

finally

{

}

        A  当在函数内部出现了throw抛出异常对象,那么就必须要给对应的处理动作。要么在内部try catch处理。要么在函数上声明让调用者处理。

总结:一般在函数上声明异常,便于提高安全性,让调用处进行处理,不处理编译失败。

Exception有一个特殊的子类异常叫做RuntimeException运行时异常(不检测异常)。

  如果在函数内发生抛出该异常,函数上可以不用声明,编译一样通过。

  如果在函数上声明了该异常,调用者可以不用进行处理。编译一样通过。

  之所以不用在函数上声明,是因为不希望调用者处理。当该异常发生,希望程序停止,因为在运行时出现了无法继续运算的情况,希望停止程序后,对代码进行修正。

RuntimeException下有很多子类,如空指针异常, indexOutBoundException等。

总结:RunTimeException很特殊的异常,它的运行会引发程序停止。

  B 捕获到的异常处理操作

     getMessage():输出错误性质。

     toString():给出异常的类型和性质

     printStackTrace():指出异常的类型、性质、栈内存跟踪信息以及在程序中的出现位置等。是虚拟机默认处理方式。

     printStackTrace(printStream s):通常用该方法将异常内容保存在日志文件中,以便查阅。

5、自定义异常

     使用Java内置的异常类可以描述编程时出现的大部分异常情况,但有一些问题,java是没有定义的,这就需要我们自己自定义异常类来处理我们代码中出现的特有异常。

     用户自定义异常类,只需要继承Exception或者它的子类即可。

  在程序中使用自定义异常类,有以下几个步骤:

 1)创建自定义异常类

 2)在方法中通过throw关键字抛出异常对象

 3)如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理,否则在方法的声明处通过throws关键字指明要给方法调用者抛出的异常,继续进行下一步操作。

4)在出现异常方法的调用者中捕获并处理异常。

   但是自定义异常,java是不清楚的,通过toString方法打印出来的只有异常类名,不会显示异常信息,如何显示异常信息呢?

  只要在自定义异常类的构造函数内,显示调用父类的构造函数初始化即可。

如:

 

  

 如果自定义异常的发生,导致无法再继续进行运算,可以让自定义异常继承RuntimeException

 异常体系有一个特点,因为异常类和异常对象都需要被抛出,他们都具备可抛性,这个可抛性是Throwable这个体系中的独有特点。只有这个体系中的类和对象才可以被throwsthrow操作。

6、对多异常的处理

有可能发生不止一个问题,那么我们在函数上的声明就不止一个了。

例如

 int divint a,int bthrows Exception  {

   int []arr=new int[a];

   System.out.println(arr[a]);

   return a/b;

   }

这个例子中,不仅可能发生除数为零的异常,也有可能发生数组角标越界异常。

   而int divint a,int bthrows Exception这种声明并不好,得声明的具体一些,处理也会具体。

1) 声明异常时,建议声明更为具体的异常,这样处理得可以更具体。

      int divint a,int bthrows ExceptionArrayIndexOutOfBoundsException

   {

     int []arr=new int[a];

     System.out.println(arr[a]);

      return a/b;

   }

这样就有两个异常,除零异常和数组脚标越界异常。声明几个异常,就对应处理几个异常。

   try

{

 

 }

catch(ArithmeticException e)

{

 }

catch(ArrayIndexOutOfBoundsException e)

{

 }

能够同时发生异常吗?不可能,脚标一越界,就不再计算a/b了。

不写那么多catch,写一个catch

catchException e

{

System.out.println(e.toString());

}

运行,符合初衷,这里就运用了多态。

但是它处理没有针对性,实际开发,都要有针对性的catch,几个异常,就写几个catch

有没有可能发生这两者意外的异常呢?有可能。那可以不可以 用Exception ecatch来捕捉未知的异常呢

可以。但是有不好的地方

     a.你都不知道发生什么,处理不会具体。而程序还在运行当中。

如果发生了两者以外的第三种异常,这时,理应做的事是,程序停掉。

我们得知道,到底是哪里出了问题。

     b.有三个catch块,前两个是无序的,如果非得把第三个catch块放在第一位,那么会出现什么问题呢?

如果程序出现问题,第一个catch块都能处理,导致第二个和第三个catch块出现失去了该有的意义,永远也执行不到。

2)对方声明几个异常,就对应几个catch块。不要定义多余的catch块。

7、异常处理原则

   1)如果多个catch块中的异常出现继承关系,父类异常catch块放在最后。

    

  2)能处理就处理,不能处理就抛出去。两种方式,要么try,要么throw

      如果该异常处理不了,但并不属于该功能出现的异常。可以将异常转换后,在抛出和该功能相关的异常。      或者异常可以处理,当需要将异常产生后和本功能相关的问题提供出去,让调用者知道并处理。也可以将捕获异常处理后,转换新的异常。

try

{

  throw  new AException();

}

catch(AException e)

{

   throw new BExeption();

}

  3)建议在进行catch处理时,catch中一定要定义具体处理方式。

 不要简单定义一句,e.printStackTrace();,也不要简单就书写一条输出语句。

做法:

把问题用一个文件记录下来,我们称之为异常日志文件。管理人员就会经常看这些日志,调试程序。

8、异常的注意事项

     1)问题在内部被解决就不需要声明。

     2catch块是用于处理异常。如果没有catch就代表异常没有被处理,如果该异常是检测时异常。那么必须声明。如果catch块中又抛出异常,却没有相应的处理,那么编译还是失败的,因为问题还是没有处理。

     3)在子父类覆盖时:

        A子类抛出的异常必须是父类的异常的子类或者子集。

        B 如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛。

9、异常的好处:

   1)将问题进行封装

   2)将正常流程代码和问题处理代码相分离,方便阅读   

练习:

   

 

三、包

 1包的由来

   一个文件夹下只能存在一个同名文件。当我们确实存在两个同名文件,如何区分呢?

用两个文件夹来区分。那么如果类同名了,我们也是如此用两个文件夹来区分,在java里,这叫做包。

定义包用关键字package,包名所有字母都要小写。

 2、包的作用及规则

   1) 对类文件进行分类管理。可以把源文件和运行文件相分离

   2) 给类提供多层命名空间。简单的说就是名称空间。辽宁的沈阳和吉林的沈阳。若只说沈阳,就会混肴。需要全称。

   3)写在程序文件的第一行。

   4) 类名的全称是   包名.类名。

   5) 包也是一种封装形式。

例如

 package pack;

 class Demo

{}

如果有一天有改动了

javac  -d  .  Demo.java   这是将Demo.java存到当前目录下的包中。

java   pack.Demo

我们也可以放到其他目录下

比如:javac -d c:\myclass  Demo.java

3、包与包之间的访问

   有了包,我们访问空间就变大了,一个包中会有很多类,不同包之间的类该如何访问呢?

 

 

 

 

原因:类名写错。应该是packa.DemoA

格式:包名.类名

 

 

 

 

编译失败。需要设置环境变量

set classpath=c:\myclass

注意:不需要写到packclasspath只要指向包的父目录即可。

再进行编译

packa.DemoApacka中不是公共的,无法从外部软件包中对其进行访问

 packa.DemoA d=new packa.DemoA();

错误原因:有了包,范围变大,一个包中的类要被访问,必须要有足够大的权限。所以被访问的类要被public修饰

 

 

错误原因:类公有后,被访问的成员也要公有,才可以被访问。

所以

public void show(){...}

 

运行正常。

到这,我们发现,包也是一种封装方式。

一个包中有十个类,有九个类是默认修饰的,一个类是public修饰的,也就是只有这一个类是对外提供的,其他九个类是内部使用的。

总结:

   1) 包与包之间进行访问,被访问的包中的类以及类中的成员,需要公有public修饰。不同包中的类是可以有关系的。如:互相继承。

例如:

 package packb;

public class DemoB

{

   public void method(){}

}

package packa;

public class DemoA extends packa.DemoB{}

 

编译运行,没问题.说明继承是没有问题的。

接下来

Main

我不再建立DemoA的对象,我建立DemoB的对象。

packageDemo.java

class packageDemo

{

   public static void main(String[]args)

   {

   // DemoA d=new DemoA();

  //d.show();

   packb.DemoB d=new packb.DemoB();

d.method();    

    }

}

 

运行正常。

pack包中的类和packa包中的类都可以访问packb包中的类。

packb DemoB

|--packa DemoA

 

pack packageDemo  可以访问DemoADemoB

那继承还有什么意义呢?

Java给包与包之间的子类提供了一个特殊权限。

package packb;

public class DemoB

{

  protected void method()

{

  System.out.println(demoB method run);

}

}

这是父类特殊的权限,叫做保护。

再编译失败,必须是子类才能访问,否则不能访问。

 

  2) 不同包中的子类还可以直接访问父类中被protected权限修饰的成员。 

 

至此,包与包之间可以使用的权限只有两种:public  protected

  权限总结

按权限从大到小排列

public——protected——default(默认权限,也就是什么都不写)——private

同一个类中  public  protected  default  private

同一个包中  public  protected  default

子类        public  protected  (不同包中默认权限不行

不同包中    public   

在同一个包中protected的作用不太大,只限于禁止覆盖。

例如

package packb;

public class DemoB

{

  protected void method()

   {

    System.out.println(demoB method run);

    }

}

class C extends DemoB

{

  void method(){}//是不可以覆盖的。因为父类method方法中有protected修饰权限不够大

 protected void method(){}//可以覆盖,

接下来,如果我们在packageDemo中的main函数中访问package Packb中的两个类,可以吗?

不可以,因为加了public以后,类名要和java文件名一致。

记住:

一个文件里面,不能出现两个以上的public修饰的公有类或者接口。

要把DemoC摘开,另外放在一个文件,但是还在同一个包中。

package packb;

public  class DemoC

{

  public void  method(){}

}

存为Democ.java

编译  

 

 

   

3) 包里面还可以有包

例如

Package packb.haha.hehe.heihei;

在文件夹里面是这样的

Packb

   |----haha

         |---hehe

             |-----heihei

                    |----DemoC.class

这是多层包目录

里面的包称为子包。

我想要在packageDemo.java中的main函数中创建DemoC对象,怎么办呢?

Packb.haha.hehe.heihei.DemoC c=new packb.haha.hehe.heihei.DemoC();

太麻烦。为了简化类名的书写,就使用一个关键字import(导入的意思)

例如

import packb.haha.hehe.heihei.DemoC;

class packageDemo

{

   public static void main(String[]args)

     {

            DemoC c=new DemoC();

       }

}

那么如果我们heihei包目录下有很多类,都需要导入,怎么办呢?

Import  packb.haha.hehe.heihei.*;

heihei包目录下的所有类都导入当前目录中来。Import导入的是包中的类

假如

 E:\javapram\packb\DemoA.class

 E:\javapram\packb\haha\DemoZ.class

如果我这样写:import packb.*;

这里,我能不能建立DemoZ的对象呢?不可以。

这样写,我仅仅是把packb中的类导入。

如果要导入DemoZ,必须

import packb.haha.*;

A  建议不要写通配符*,需要用到包中的哪一个类,就导入哪一个类,这样最好。

Eclipse中,用ctrl+shift+o 把用到的类全部自动导入。

import packb.*;

import packb.haha.*;

两个包中都有DemoC.class类文件,同名类文件.这时,DemoC c=new DemoC();这样写是绝对不允许的。因为搞不清楚是谁的DemoC,必须加包名。 

为什么建立包,一个原因就是为了类不重名,但是如果包名重名了呢?没戏,所以,为了不使包名重名,我们定义包名的时候,是这样定义的。 

B   建议定义包名不要重复,可以url来完成定义,因为url是唯一的。例如:

传智播客的网站www.itcast.cn

如:package  cn.itcast.demo

---Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值