您的位置:首页 > 职场人生

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

2015-04-27 20:52 441 查看
------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)不可以被成员修饰符修饰。如public、private、static等修饰符修饰,因为修饰符只能修饰成员,它的作用域被限定在了声明这个局部内部类的代码块中
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中传入的是4和0,就会出现除0异常,也就是依照数学规则,除数不能为0.这就是我们所说的异常。
其实就是java对不正常情况进行描述后的对象体现。
一个问题,里面有问题的由来,问题的信息,问题的结果。把这些封装成对象以后,都可以通过对象内部的功能进行操作。
2、异常的体系划分
对于问题的划分:分为两种:一种是严重的问题,一种是非严重的问题。
对于严重的,java通过Error类进行描述,对于非严重的,java语言通过Exception类进行描述。
比如生活中的疾病,可以封装成对象,疾病可以分为两类,可治愈和不可治愈。可治愈就是Exception,不可治愈就是Error
1)异常体系Throwable
无论error 或者exception都具有一些共性内容。比如:不正常情况的信息,引发原因等。
有了共性内容,我们知道,可以向上抽取,形成父类,叫做Throwable。
Throwable
|-----Error
|------Exception
Error和exception下面都有很多子类,异常体系中的类有一个特点,都是以父类为后缀的。比如
Runtimeexception、IOexception等等。
异常体系的特点: 异常体系中的所有类以及建立的对象都具备可抛性,也就是说可以被throw和throws关键字所操作。只有异常体系具备这个特点。
2)Error
通常指出现重大问题如:运行的类不存在或内存溢出等。这是虚拟机告诉你的。因为超出虚拟机处理范围了。Java用Error类描述Java运行系统中的内部错误及资源耗尽的错误。Error类表示问题很严重,仅靠修改程序代码是不能修复,也被成为致命类。
例如:byte[]arr=new byte[1024*1024*1024];
内存溢出 OutOfMemoryError
对于error,通常不编写针对代码对其处理。发生error时,应该建议终止程序。
3)Exception
发生这种异常时,表示通过处理,可以保证程序正常运行,也称为非致命类。对于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语言的异常处理器由try、catch、finally3个语句块构成。
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这个体系中的独有特点。只有这个体系中的类和对象才可以被throws和throw操作。
6、对多异常的处理
有可能发生不止一个问题,那么我们在函数上的声明就不止一个了。
例如
int div(int a,int b)throws Exception {
int []arr=new int[a];
System.out.println(arr[a]);
return a/b;
}
这个例子中,不仅可能发生除数为零的异常,也有可能发生数组角标越界异常。
而int div(int a,int b)throws Exception这种声明并不好,得声明的具体一些,处理也会具体。
1) 声明异常时,建议声明更为具体的异常,这样处理得可以更具体。
int div(int a,int b)throws Exception,ArrayIndexOutOfBoundsException
{
int []arr=new int[a];
System.out.println(arr[a]);
return a/b;
}
这样就有两个异常,除零异常和数组脚标越界异常。声明几个异常,就对应处理几个异常。
try
{

}
catch(ArithmeticException e)
{
}
catch(ArrayIndexOutOfBoundsException e)
{
}
能够同时发生异常吗?不可能,脚标一越界,就不再计算a/b了。
不写那么多catch,写一个catch
catch(Exception e)
{
System.out.println(e.toString());
}
运行,符合初衷,这里就运用了多态。
但是它处理没有针对性,实际开发,都要有针对性的catch,几个异常,就写几个catch
有没有可能发生这两者意外的异常呢?有可能。那可以不可以 用Exception e的catch来捕捉未知的异常呢
可以。但是有不好的地方
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)问题在内部被解决就不需要声明。
2)catch块是用于处理异常。如果没有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
注意:不需要写到pack。classpath只要指向包的父目录即可。
再进行编译
packa.DemoA在packa中不是公共的,无法从外部软件包中对其进行访问
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 可以访问DemoA和DemoB
那继承还有什么意义呢?
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培训、期待与您交流! -------

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: