JavaSE实战——面向对象(下) 异常,包,四种访问控制权限

时间:2021-05-10 15:21:56

    转载请声明出处:http://blog.csdn.net/zhongkelee/article/details/45273681

    我们接着上一篇博客“面向对象(中)”的内容,继续往下说。

异常

    异常:就是不正常。程序在运行时期出现的不正常情况。其实就是程序中出现的问题。(编译时期 只检查语法错误)
    这个问题按照面向对象思想进行描述,并封装成了对象。
    因为问题的产生有产生的原因、有问题的名称、有问题的描述等多个属性信息存在。当出现多属性信息最方便的方式就是将这些信息进行封装。异常就是java按照面向对象的思想将问题进行描述和对象的封装。这样就方便于操作问题以及处理问题。 
    出现的问题有很多种,比如角标越界,空指针等都是。就对这些问题进行分类。而且这些问题都有共性内容比如:每一个问题都有名称,同时还有问题描述的信息,问题出现的位置,所以可以不断的向上抽取。形成了异常体系

    -------------java.lang.Throwable:
    Throwable:定义了对于问题的共性的功能。(两种异常的父类)
        |--Error:由系统底层发生的,告诉JVM,JVM告诉使用者。

                 不编写针对性的代码进行处理。直接修改代码。
        |--Exception:由JVM发生,并告诉给使用者。可以进行针对性的处理。
    这个体系中的所有类和对象都具备一个独有的特点;就是可抛性
    可抛性的体现:就是这个体系中的类和对象都可以被throwsthrow两个关键字所操作。
    总结:
    1.运行时发生。
    2.符合面向对象,将问题描述并封装成对象。
    3.error和exception的区别。

<span style="font-size:14px;">class ExceptionDemo{
public static void main(String[] args){
//int[] arr = new int [1];//ArrayIndexOutOfBoundsException
//arr = null;//NullPointerException
int[] arr = new int[1024*1024*100];//OutOfMemoryError
System.out.println(arr[1]);
/*
异常出现,在内存中发生的过程:
输出语句发生问题时,jvm就将这个已知的问题封装成了对象,
throw new ArrayIndexOutOfBoundsException(1);将问题抛给调用者main函数。
main没有针对性的处理方式,main就继续往外抛给调用者jvm,jvm就使用了默认的处理方式,
将问题的名称+信息+位置在控制台上显示出来,让调用者(启用jvm的人)看到,然后结束程序。
抛的原因在于,出现问题,如果自己处理不了,就要抛出去,让对方(调用者)知道。
*/
System.out.println("over");//前一句发生了异常,程序已经结束,这一句就不执行了。
}
}</span>

    异常的处理方式有两种:1.抛出,2.捕捉

    1.抛出(throws)
        到问题不进行具体的处理,而是继续抛给调用者。
        其实就是在函数上通过throws关键字声明异常,告诉调用者处理。
  2.捕捉(try catch)
        针对性的处理方式:捕获!
        try{
              //有可能发生异常的代码(类似于安检)
        }
        catch(异常类 变量){
             //这是真正的捕获,处理异常的代码
        }
        finally{
             //一定会被执行的代码
        }
class Demo{
int div(int a, int b)throws Exception{//声明异常
if (b == 0)
throw new ArithmeticException("除数为0,废了!");//抛出异常对象-->手动封装
return a/b;//throw new ArithmeticException("/ by zero");抛给调用者。-->自动封装
}
}
class ExceptionDemo2{
public static void main(String[] args){//throws Exception
Demo d = new Demo();
try{
int num = d.div(4,0);
System.out.println("num = "+num);
}
catch(Exception e){//Exception e = new ArithmeticException("/ by zero");
//处理这个对象,可以使用该对象的方法。
System.out.println("异常啦!");
System.out.println(e.getMessage());//异常信息
System.out.println(e.toString());//异常名称+异常信息
e.printStackTrace();//异常名称+异常信息+异常位置。jvm默认处理收到的异常,就是调用了这个方法,将信息显示在屏幕上。
}
System.out.println("over");//这句话正常执行。因为catch中的内容是异常处理,但并没有结束程序。异常处理完,程序继续往下执行。(区分于jvm默认的异常处理:打印并结束程序)
}
}

    在开发中定义功能时,如果编写者知道该功能会出现一些问题。而这个问题很容易来自于调用者传递的参数。而导致功能无法运行。这时发生的问题就应该让调用者知道。并最好让调用者有预先的处理方式。所以在定义功能时,需要在功能上对有可能发生的问题进行声明、标识。这样调用者就可以在使用这个功能的时候,预先给出处理方式。

    如何标识呢?需要使用关键字:throws。

    用法:throws 异常类名,异常类名...

    这样标识后,调用者在使用该功能时,就必须要处理,否则编译失败。

    throw和throws有什么区别呢?
    1.位置不同
      throws用在函数上,后面跟的是异常类,可以跟多个。编译时期。

      throw用在函数内,后面跟的是异常对象。运行时期。

    2.功能不同

      throws用来声明异常,让调用者知道该功能有可能出现的问题,并由调用者可以给出预先的处理方式。
      
throw抛出具体的异常对象。执行到throw,功能就已经结束了。就跳转到了调用者,并将具体的异常对象也抛给了调用者。

void show(){
if (1>0){
throw new Exception();
System.out.println("haha");//throw已经结束该功能,这句话执行不到。
}
return;
}
    也就是说throw语句独立存在时,下面不要定义其他语句,因为执行不到。

    异常体系的独有特点:可抛性
    Exception
      |--Error
      |--Exception
    异常体系最大的特点就是体系中的类以及类产生的对象,都具备可抛性,可抛性的意思是可以被throws和throw所操作。

    异常处理的原则:
    1.功能内部有异常throw抛出,功能上一定要throws声明,让调用者知道。
      内部抛什么,功能上就声明什么。
      声明的目的就是为了让调用者处理(抛出或者捕获),如果调用者不处理,编译失败。
    2.特殊情况:
      当函数内通过throw抛出了RuntimeException及其子类的异常对象时,函数上可以不用throws声明。
      不声明的目的就是不让调用者处理。让调用者的程序停止。要对代码进行修改。
      运行时异常发生,已经无法再让程序继续运行。不声明,调用者就不知道,就避免了调用者胡乱处理异常,导致安全隐患。

    运行时异常是指程序正常运行期间发生的一些异常,导致程序无法正常执行,所以停止程序(实际停止的是所在线程)。
    Error是底层系统已经不能支持运行才挂的。

class Demo{
int div(int a, int b)throws Exception{
if (b == 0)
throw new Exception("除数为0!");
return a/b;
}
}
class ExceptionDemo3{
public static void main(String[] args){//throws Exception
Demo d = new Demo();
try{
int num = d.div(4,0);
System.out.println("num = "+num);
}
catch(Exception e){
System.out.println(e.toString());
}
System.out.println("over");
}
}
        Exception分两种:
       1.编译时会被检测的异常。只要是Exception及其子类都是编译时被检测的异常。(编译器先检查语法问题,然后检查程序的基本安全问题,如果功能内部有异常抛出,功能外部却没有声明,则编译失败。)
       2.运行时异常。RuntimeException及其子类,也就说这个异常是编译时不被检查的异常。

    编译时被检查的异常和运行时异常的区别:
    1.编译被检查的异常在函数内被抛出,函数必须要声明,否编译失败。
      声明的原因:是需要调用者对该异常进行处理。
    2.运行时异常如果在函数内被抛出,在函数上不需要声明。
      不声明的原因:不需要调用者处理,运行时异常发生,已经无法再让程序继续运行,所以,不让调用处理的,直接让程序停止,由调用者对代码进行修正。

class ExceptionDemo4{
public static void main(String[] args){
int[] arr = new int[3];
arr = null;
System.out.println("element:"+getElement(arr,-2));
}
public static int getElement(int[] arr, int index){//throws ArrayIndexOutOfBoundsException
if (arr == null)
throw new NullPointerException("数组实体不存在");
if (index<0 || index>=arr.length)
throw new ArrayIndexOutOfBoundsException("角标"+index+"不存在");
int element = arr[index];
return element;
}
}
    1.如果函数声明了异常(throws ArrayIndexOutOfBoundsException),调用者就要有处理方式,要么try捕获,要么throws继续抛;
    2.如果函数没有声明异常,此时调用者不知道会有异常发生。
      也就是说,函数中出现的异常问题将会导致调用者程序无法继续执行下去,必须让程序停下来,然后修正程序才可以。
      那么就不声明,从而来停止程序,给调用者修正代码的机会。
      这样做的目的就是不让调用者胡乱处理异常,导致后续程序还在执行,看不到问题所在。

    运行时异常,我们应用的比较多。内部有抛出,外部不声明,让调用者知道问题所在,这就是运行时异常的好处。

自定义异常

    当开发时,项目中出现了java中没有定义过的问题时,这时就需要我们按照java异常建立思想,将项目的中的特有问题也进行对象的封装。这个异常,称为自定义异常。

    自定义异常的步骤:
    1:定义一个子类继承Exception或RuntimeException,让该类具备可抛性。
    2:通过throw 或者throws进行操作。

    自定义异常的好处:提高阅读性。

    例如:定义一个功能可以实现除法运算。但是除数不可以为负数。

//将负数为除数的问题描述,自定义异常。
class FuShuException extends RuntimeException{
FuShuException(){
super();
}
FuShuException(String message){
super(message);
}
}
class Demo{
int div(int a, int b){
if (b < 0)
throw new FuShuException("负数不可以作为除数");
if (b == 0)
throw new ArithmeticException("被零除了!");
return a/b;
}
}
class ExceptionDemo5{
public static void main(String[] args){
Demo d = new Demo();
try{
int num = d.div(4,-1);
}catch(Exception e){
System.out.println(e.toString());
}
System.out.println("over");
}
}

finally

    finally的作用是:无论是否有异常发生,都要对资源进行释放。资源释放动作就定义在finally代码块中。

class ExceptionDemo6{
public static void main(String[] args){
try{
int num = 4/0;
System.out.println("num = "+num);
}
catch(Exception e){
System.out.println(e.toString());
return;
//System.exit(0);//退出jvm。只有这种情况,finally也不执行。
}
finally{
System.out.println("finally");
}
System.out.println("over");
}
}

try,catch,finally的几种组合方式

    1.没有资源需要释放,仅仅是处理异常。

try{

}catch(){

}

    2.一个try多个catch,一般对应的是被调用的函数,抛出多个异常的情况,分别处理。

try{

}
catch(ArrayException e){

}
catch(Exception e){

}
catch(){

}
    注意:在多catch语法上特殊的地方,如果多catch中的异常类存在子父类,父类的catch一定要放在子类的下面,否则编译失败。

    3.不一定要处理异常,但是有资源需要释放。

try{

}
finally{

}
    这种情况,如果出现异常,并不处理,但是资源一定关闭,所以try  finally集合只为关闭资源。

    记住:finally很有用,主要用户关闭资源。无论是否发生异常,资源都必须进行关闭

          System.exit(0); //退出jvm,只有这种情况finally不执行。

void show()throws Exception{//如果异常没有被处理,就要声明(throws)出去,让调用者处理。
try{
throw new Exception();
}
//catch(Exception e){
//throw e;//此时,catch到的异常e,又被抛了出去,还是要在函数上声明throws Exception的。
//}
finally{

}
}

    4.一般情况

try{

}catch(){

}
finally{

}

    异常小结:其实就是将问题封装成对象,并抛给调用者。如果声明了,就需要调用者处理(继续声明throws or捕获try&catch)。

    什么时候声明?什么时候捕获?
    功能内部可以解决,就捕获;不能解决或者解决了还必须告诉调用者问题,这时就应该声明。

    举例:
    毕老师用电脑上课。
    上课过程中会发生问题:比如电脑蓝屏,电脑冒烟了。
    需要对问题进行描述。

//蓝屏是可处理的,继承Exception
class LanPingException extends Exception{
LanPingException(){
super();
}
LanPingException(String message){
super(message);
}
}
//冒烟
class MaoYanException extends Exception{
MaoYanException(){
super();
}
MaoYanException(String message){
super(message);
}
}
//课时无法进行
class NoPlanException extends Exception{
NoPlanException(){
super();
}
NoPlanException(String message){
super(message);
}
}
class Computer{
private int state = 2;//用于描述电脑状态
public void run() throws LanPingException,MaoYanException{
if (state == 1)
throw new LanPingException("电脑蓝屏啦!");
if (state == 2)
throw new MaoYanException("电脑冒烟啦!");
System.out.println("电脑运行");
}
public void reset(){
state = 0;
System.out.println("电脑重启");
}
}
class Teacher{
private String name;
private Computer comp;
Teacher(String name){
this.name = name;
comp = new Computer();
}
public void prelect() throws NoPlanException{
try{
comp.run();
System.out.println("讲课");
}
catch(LanPingException e){
System.out.println(e.toString());
comp.reset();
//继续讲课.
prelect();
}
//封装本层异常,对外暴露调用者能处理的异常,这就是异常转换。
catch(MaoYanException e){//MaoYanException e = new MaoYanException("");
System.out.println(e.toString());
test();
throw new NoPlanException("课时进度停止");//继续抛,但进行异常转换。
}
}
public void test(){
System.out.println("学生自己练习");
}
}
class ExceptionTest{
public static void main(String[] args){
Teacher t = new Teacher("毕老师");
try{
t.prelect();
}
catch(NoPlanException e){
System.out.println("换老师");
}
}
}

异常转换

    当出现的异常是调用者处理不了的,就需要将此异常转换为一个调用者可以处理的异常抛出。

class APP{
public void show(){//该调用者不会处理数据库异常。只负责数据提取和显示。
try{
new DBTool().operate();
}
catch(NoValueException e){

}
}
}
class DBTool{
public void operate()throws NoValueException{
//连接数据库

try{
//数据操作,发现有异常 throw new SQLException()
}
catch(SQLException e){
//解决了数据库异常。但是出现的异常要返回给调用者知道。不过,这里要问题封装,异常转换,转换成调用者可以处理的异常。
throw new NoValueException();
}
finally{
//关闭数据库
}
}
}

覆盖中异常的应用

    Exception
        |--AException
            |--AAException
        |--BException
    当异常出现后,在子父类进行覆盖时,有了一些新的特点:
    子类方法覆盖父类方法,如果父类的方法抛出了异常,那么子类的方法要么不抛出异常要么抛出父类异常或者该异常的子类,不能抛出其他异常。
    如果父类方法抛出多个异常,那么子类在覆盖时只能抛出父类异常的子集。
    原则:就是子类的异常必须要在父类的异常处理控制中。

class AException extends Exception{

}
class BException extends Exception{

}
class AAException extends AException{

}
class Fu{
void show()throws AException{
System.out.println("fu show");
}
}
class Zi extends Fu{
void show()throws AAException{//子类不可以抛出BException。
System.out.println("zi show");
}
}
class Tool{
void method(Fu f){
try{
f.show();
}
catch(AException e){

}
}
}
class ExceptionDemo7{
public static void main(String[] args){
Tool t = new Tool();
t.method(new Zi());
}
}

    注意:
    有一种情况,只能try不能throws。
    如果父类或者接口中被覆盖的方法没有抛出过异常,那么子类是不可以抛出异常的,如果子类的覆盖的方法中出现了异常,只能try不能throws。如果这个异常子类无法处理,已经影响了子类方法的具体运算,这时可以在子类方法中,通过throw抛出RuntimeException异常或者其子类,这样,子类的方法上是不需要throws声明的。

interface Inter{
public abstract void show();
}
class Demo implements Inter{
public void show(){//不可以声明异常,子类方法不可以比覆盖了的没有声明异常的父类方法更有问题。
try{
throw new Exception();
}
catch(Exception e){
throw new RuntimeExceptiom("");//将编译时检测异常,转换成运行时异常,这样就不用声明,并且可以停止程序,方便调用者修改代码。
}
}
}

    定义包用package关键字。

    1.对类文件进行分类管理;2.给类文件提供多层命名空间。

    注意:包名的写法规范:所有字母都小写。

package mypack;

/*
packa\TestA.class
\DemoA.class
\A.class
\haha\zz.class
*/
import packa.DemoA;//导入了packa包中的DemoA类。
import packa.TestA;
import packa.A;
import packa.*;//*通配符;import导入的是当前包下的类,不包含子包。
import packa.haha.*;//导入packa包中haha子包中的类。

import packa.Demo;
import packb.Demo;//当不同包中的类重名了,这是即使导入了,也必须在使用时明确该类的包名。

class PackageDemo{
public static void main(String[] args){
//packfu.DemoFu d = new packfu.DemoFu();
//d.method();
//packa.DemoA d1 = new packa.DemoA();
DemoA d1 = new DemoA();
d1.show();
System.out.println("Hello package!");
}
}

易出现的问题汇总:

---------------------------------------------------------------

问题1:
PackageDemo.java:5: 错误: 找不到符号
                DemoA d = new DemoA();
                ^
  符号:   类 DemoA
  位置: 类 PackageDemo
PackageDemo.java:5: 错误: 找不到符号
                DemoA d = new DemoA();
                              ^
  符号:   类 DemoA
  位置: 类 PackageDemo
2 个错误

原因:类名写错。有了包以后,类都有包的所属,所以必须明确其包名。

类的名称应该是:包名.类名

---------------------------------------------------------------

问题2:
PackageDemo.java:5: 错误: 程序包packa不存在
                packa.DemoA d = new packa.DemoA();
                     ^
PackageDemo.java:5: 错误: 程序包packa不存在
                packa.DemoA d = new packa.DemoA();
                                         ^
2 个错误

原因:packa包没有存放在当前目录下。需要告诉jvm它的位置。
解决:设置classpath。将包所在父目录定义到classpath变量中即可。
(set classpath查看,set classpath=清除,setclasspath=.;%classpath%设置)

---------------------------------------------------------------

问题3:
PackageDemo.java:5: 错误: DemoA在packa中不是公共的; 无法从外部程序包中对其进行访问
                packa.DemoA d = new packa.DemoA();
                     ^
PackageDemo.java:5: 错误: DemoA在packa中不是公共的; 无法从外部程序包中对其进行访问
                packa.DemoA d = new packa.DemoA();
                                         ^
2 个错误

原因:被访问的包中的类,权限不够。
解决:被访问的包中的类,用public修饰。

---------------------------------------------------------------

问题4:
PackageDemo.java:6: 错误: show()在DemoA中不是公共的; 无法从外部程序包中对其进行访问
                d.show();
                 ^
1 个错误

原因:被访问的方法也必须是public。

---------------------------------------------------------------

问题5:
错误: 找不到或无法加载主类 PackageDemo

原因:有了包以后,类都有所属,类名也随之变化。类的全名称是 包名.类名

---------------------------------------------------------------

总结:包与包之间的类进行访问,被访问的包中的类必须是public的,被访问的包中的类的方法也必须是public的。

package packfu;

public class DemoFu{
protected void method(){//protected修饰的成员只给子类使用。
System.out.println("demoFu method run");
}
}
package packa;public class DemoA extends packfu.DemoFu{public void show(){method();System.out.println("DemoA show run");}}/*DemoA.java:5: 错误: 找不到符号                method();                ^  符号:   方法 method()  位置: 类 DemoA1 个错误原因:default修饰成员,是不可以被处于不同包中的子类访问到的,因为被包封装了。解决:可以使用public(最大权限)、protected(仅限子类访问)修饰成员,使得不同包的别的类访问。*/

Java中的访问权限控制
    在Java中,提供了四种访问权限控制:默认访问权限(包访问权限)defaultpublicprivate以及protected
    注意,上述四种访问权限,只有默认访问权限default和public能够用来修饰类。修饰类的变量和方法四种权限都可以。(本处所说的类针对的是外部类,不包括内部类)
1.修饰类
    默认访问权限(包访问权限)default:用来修饰类的话,表示该类只对同一个包中的其他类可见。
    public:用来修饰类的话,表示该类对其他所有的类都可见。(注:一个.java文件中至多只能有一个public类,并且如果有该public类,类名必须和文件名完全一致)

2.修饰类的方法和变量
    默认访问权限(包访问权限)default:如果一个类的方法或变量被包访问权限修饰,也就意味着只能在同一个包中的其他类中显示地调用该类的方法或者变量,在不同包中的类中不能显示地调用该类的方法或变量。
    private:如果一个类的方法或者变量被private修饰,那么这个类的方法或者变量只能在该类本身中被访问,在类外以及其他类中都不能显示地进行访问。
    protected:如果一个类的方法或者变量被protected修饰,对于同一个包的类,这个类的方法或变量是可以被访问的。对于不同包的类,只有继承于该类的子类才可以访问到该类的方法或者变量。(也就是说,在不同包中创建该父类对象,不能调用父类中的protected方法)
    public:被public修饰的方法或者变量,在任何地方都是可见的。

                public  protected  default  private
    同一个类中    ok       ok        ok       ok
    同一个包中    ok       ok        ok   
    子类          ok       ok
    不同包中      ok

    包与包之间只有两种权限可以用,public、protected(只给子类使用)。

    一个.java文件中,只能有一个package,一个public class(且必须和.java文件同名),可以有多个import,多个default class。

import关键字

    解决包与包之间访问时,类名变长的问题。为了简化,使用了一个关键字:import,可以使用这个关键字导入指定包中的类。
    记住:实际开发时,到的哪个类就导入哪个类,不建议使用*.
    作用:简化类名的书写。
    导包原则:用到哪个类,就导入哪个类。
    import packa.*;//这个仅仅是导入了packa当前目录下的所有的类。不包含子包。
  import packa.abc.*;//导入了packa包中的子包abc下的当前的所有类。
    如果导入的两个包中存在着相同名称的类。这时如果用到该类,必须在代码中指定包名。

    常见的软件包:
    java.lang:language java的核心包,Object System  String Throwable jdk1.2版本后,该包中的类自动被导入。
    java.awt:定义的都是用于java图形界面开发的对象。
    javax.swing:提供所有的windows桌面应用程序包括的控件,比如Frame,Dialog,Table,List等等,就是java的图形界面库。
    java.net:用于java网络编程方面的对象都在该包中。
    java.io:input  output 用于操作设备上数据的对象都在该包中。比如:读取硬盘数据,往硬盘写入数据。
    java.util:java的工具包,时间对象,集合框架。
    java.applet:application+let 客户端java小程序。server+let——>servlet服务端java小程序。

Jar

    Jar:java的压缩包。
    jar -cfv my.jar pack//压缩
    jar -tfv my.jar//列出档案目录
    jar -xfv my.jar//解压
    set classpath=.\my.jar
    java pack.JarDemo

package pack;

class JarDemo{
public static void main(String[] args){
System.out.println("hello jar!");
}
}

 

好了,J2SE的面向对象部分就介绍到这里,本系列后一篇博文会介绍Java中的多线程技术。

有任何问题请和我联系,共同进步:lichunchun4.0@gmail.com

转载请声明出处:http://blog.csdn.net/zhongkelee/article/details/45273681