Java基础:内部类访问规则|静态内部类|内部类定义规则|匿名内部类|异常概述|异常try-catch|异常声明throws|多异常处理

    01-面向对象(内部类访问规则)

        内部类:

        从名称上直观的来分析, 这个类是已经定义到另外的类的里边啦。

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

        比如:

        访问特点:

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

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

        这就相当于孙悟空要找到牛魔王的心脏,就要先找到牛魔王再找到它的心脏,就是牛魔王.get心脏()方法。但是孙悟空如果跑到牛魔王肚子里面去了,他访问牛魔王的心脏就可以直接访问啦。

        我们可以直接访问内部类中的成员吗?

        可是如果还有一个类叫Outer2,里面也有个内部类叫Inner,怎么区分是哪个类里的内部类呢?

        所以一定要指明是哪个类里的Inner()。

        右边格式也要改一改呢,要写成酱紫:

        但是这个很少被用到,因为内部类可能会被私有,就无法直接访问了。这个只是一种格式,应该只有面试的时候可能会用到。

        类可以被私有吗?

        内部类可以被私有。当一个类是另一个类的内部类时,可以被私有。

        但是一般的类绝对不可以私有。

        为什么内部类可以直接访问到外部类的内容? 

        一个例子。

        如果想打印x=6,则:

        如果想打印x=4,则:

        如果想打印x=3,则:

        当然,如果内部类里面没有另外定义x=4和x=6,那么打印x就默认打印的是外部类的x=3。这个时候x前面有隐式的Outer.this。

        之所以内部类可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this。

    02-面向对象(静态内部类)

        访问格式:

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

        格式:

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

        即 Outer.Inner in=new Outer().new Inner();

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

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

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

                  的static成员,出现了访问局限。

                在外部其他类中,如何直接访问static内部类的非静态成员呢?

                这样即可:

                如果fuction()也静态了呢?

                这样即可: 

                但是使用频率很低。

        注意:当内部类中定义了静态成员,该内部类必须是静态的。否则会报错。

        这样才对:

        再一个例子:

        改成酱紫就OK啦:

        注意:当外部类中的静态方法访问内部类时,内部类也必须是静态的。

    03-面向对象(内部类定义原则)

        当描述事物时,事物的内部还有事物,该事物用内部类来描述。

        因为内部事务在使用外部事物的内容。

        比如心脏之于人体。

        我们的心脏不可以直接被其他人访问到,所以要私有。

        当一个类需要直接访问到另外一个类中的成员的时候,就把另外一个类写到这个类的里面。写完之后,争取把这个类封装在外部类中,不被外暴露,而是对外提供一个方法进行访问。

    04-面向对象(匿名内部类)

        我们发现,刚才写的内部类,定义在外部类的成员位置上,只有定义在成员位置上的内部类,才可以被私有或静态修饰。一般内部类是不会被公有修饰的。有没有这种情况呢?有。比较特殊的情况会出现。这个就不说啦。

        除了定义在外部类的成员位置上,内部类还可以写在其他位置上呢,比如写在外部类的方法当中:

        这个类就是局部的。但是访问规则没有变,它还是可以直接访问外部类中的成员。

        那它还能被静态所修饰吗?不能。因为静态修饰符只修饰成员,现在它是局部的,所以不能被修饰了。那它还能定义静态的成员吗?不能。因为内部类中若有静态成员,这个内部类也必须是静态的,所以它也不可以定义静态的成员。

        现在调用这个内部类中的function,因为必须要有这个内部类的对象才可以顶用它,所以要这样写:

        可以看到运行是没有问题的。

        这是内部类定义在局部位置上的一个小特点。

        为什么不把定义内部类对象放在前面呢?会报错的:

        因为如果放在前面,类定义又在后面,定义内部类对象的时候,还没有读到这个类的定义呢。所以内部类对象的定义要放在内部类的定义的后面。

        那么内部类是否可以访问它所在的方法中定义的变量y呢?会报错:

        这样就OK啦:

        内部类定义在局部时:

        1,不可以被成员修饰符修饰。

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

              但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。

        这个a可以被打印吗?

        不可以喔。

        为什么呢?

        因为a是一个变量,需要被final修饰才能访问。

        像酱紫:

        这时候就郁闷了,a不就变成常量了嘛?那底下不是又给a赋值7,这不是很矛盾吗?

        这其实没问题,把7赋值给a,这之后a就被锁住了。看,运行没有问题的:

        试试再给它赋值为8:

        或者这个形式:

        不过,对于给a赋值来说,这两种方法一点区别都没有。

        因为a并不是在对象中。它是标准的局部变量。这两种方法如果是给x赋值,就会有区别的。

        好,其实不管哪种方法,这样都是可以编译通过的,为什么呢?a不是常量吗?

        分析一波:调用method()方法,它就进栈内存了, 栈里也有a,把7赋值给a,锁住了,a就一直为7。直到out.method(7)这句话执行完了,就要出栈了,释放了。下一句话,再调用method(),又进栈了,就是新的a了,这时把8赋值给a,a锁住了。所以这样是OK的。

        那什么情况是不OK的呢?

        酱紫就不阔以哦:

        匿名内部类:

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

        2,定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。

        比如每个人都是有心脏的,在外部定义一个抽象的心脏,所有人的内部的心脏类都继承它,继承它里面的方法。

        这是一个常规的内部类:

        我们现在要把它简化成一个匿名内部类。怎么简化呢?

        绿色的部分,其实就是我们需要简化的部分。

        简化之前我们先分析一下,内部类都做了什么事情呢?

        1,它继承了一个类;2,复写父类的方法;3,创建对象;4,调用。

        这个内部类本来的名字叫Inner,现在没这个名字了,我们怎么调用它呢?

        酱紫来:

        因为是写匿名,所以这个时候Inner没有了,我们把它改成父类的名字:

        这个时候需要覆盖父类的方法,继续:

        这样就OK啦。这就是一个匿名内部类,也是一个带着内容的对象,一个很胖哒对象!

        记住:这个整体是一个对象!是一个什么对象呢?是AbsDemo的子类对象,因为只有子类才能复写AbsDemo中的抽象方法。这个整体就是绿色部分的简化写法~

        这种写法很简化,但是可能不是很好理解。但是开发中这种写法非常常见。

        所以接着上面,匿名内部类:

        3,匿名内部类的格式:    new 父类或者接口(){定义子类的内容}

        4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。可以理解为带内容的对象。

        也可以在里面写其它函数并调用哦:

        如果又想调用abc(),又想调用show()呢?酱紫: 

        但是这样太麻烦了,能不能起个名字呢?但是不是匿名类吗?不是不可以起名字吗?但是可以这样:

        这其实就是多态中的父类调用子类对象。但是第二个abc就不可以被父类调用,因为abc是子类的方法,父类中没有定义。所以这样只能调用父类方法,不能调用子类方法,感觉意义也不大。我们写匿名类就是为了简化书写,这样一来感觉更麻烦了。

        匿名内部类是有局限性的。它的出现就是为了简化书写,所以这样一来有好处也有弊端,弊端就是直接调用自己的方法还不行(就是上面那种情况),还有一个弊端,就是父类里面的方法过多的话就不要定义匿名内部类了,比如:

        匿名内部类里面就是这样的:

        这样如果你想调用的话,就会非常麻烦,里面那么多方法,阅读性已经没有了,如果每个方法里面的代码再多一点,这就是一屏(甚至好几屏)的代码,我们都没眼看了!

        所以,一般写匿名内部类,它里面的方法不会超过三个,大多两个或一个,这样匿名对象调用方法就会方便很多。

        所以,再接前面的,匿名内部类:

        5,匿名内部类中定义的方法最好不要超过3个。

        那如果没办法,父类里的方法就是那么多怎么办?那就按常规的内部类来咯,定义一个内部类,继承它,后面可以建立这个类的内部对象慢慢一个个调用~

        匿名内部类就是在方法少的情况下,可以简化书写、方便调用,里面写得太复杂就失去意义了。

        下面来一个小练习:

        分析一下这句话:

        Test类中肯定有静态成员,而且这个静态成员方法的名字叫function。

        所以,可以确定先写成这样:

        继续分析这句话:

        调用完function之后紧接着又调用了method方法,从前面Inter接口的内容可以看出,method是非静态的(因为它是抽象的所以一定是非静态的)。由此可以推出,Test调用了function的运算结果是一个对象。因为只有返回了对象,对象才能调用method方法。

        我们想想哪个对象可以调用method?必然是Inter的对象。所以返回值类型也确定了,是Inter:

        我们先用内部类来写一遍,运行是没有问题的:

        接下来用匿名内部类来写:

        总结回顾一下:

        有一种需要用匿名内部类的情况很常见,说一下:

        现在的想法是,调用show(),这个括号里面是不是要传进来一个接口类型的对象。

        我们可以怎么做呢?

        分解做的话就是,先搞一个类,去把这个接口实现一下,这个类定义在内部也行,定义在外部也行,定义完之后,把这个类的对象作为参数传给show方法。但是这么做很麻烦。

        当使用的对象是接口类型时,查看一下这个接口里的方法,如果不超过三个,我们可以定义一个匿名内部类,把匿名内部类作为参数传进去。

        酱紫:

        万一没有父类,也没有接口,就是想调用一个function方法,还可以写匿名内部类吗?

        厉害的来啦,我们可以new一个Object对象。

        对了,一定要注意:

        后面带分号:

Object对象

        后面是大括号:

Object子类对象

        好,这样就OK啦:

        然后我们还想加一个函数,这个时候就需要给它起名字:

        注意这样是不可以的哦,这时一个错误示例,以为Object类里面并没有定义这个方法,所以不可以这样调用。

    05-面向对象(异常概述)

        什么叫异常呢?就是我们所谓的不正常。

        写了一个除法的小程序:

        但有时候会传不合法的值进去,我们会发现,编译的时候没有问题,但是运行的时候出现了问题,程序不正常结束了:

        这就是我们所说的异常。

        异常:就是程序在运行时出现的不正常情况。

        异常由来:问题也是现实生活中一个具体的事物,也可以通过java的类的形式进行描述,并封装成对象。

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

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

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

                对于Error,一般不编写针对性的代码对其进行处理。(就好像得了癌症一样,只是进行保守治疗,没有什么针对性的治疗方式可以药到病除了)

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

                对于Exception可以使用针对性的处理方式进行处理。(对于感冒发烧,可以喝感冒药治好)

        所以我们重点讲能处理的部分,也就是Exception。

        一个内存溢出的例子:

        虚拟机本身有自己启动的默认空间,任何一个软件启动都会划分到这个默认空间来存放数据,但是虚拟机它的空间是有限的,就这么大,你非要开一个超过它的大小,就挂掉了。道理很简单,超出虚拟机的范围。

        虚拟机在启动的时候,可以通过它提供的参数,来将它改变成更大的空间。但是你分配多大都没用,如果数组这个空间,已经超出物理内存空间的话,再改变也没戏。就像这个问题,怎么处理也没戏。

        那Error和Exception有没有共性呢?

        有的。

        拿感冒发烧来举例,这两个疾病都有它的名称,都有发病原因。

        无论Error或者Exception都具有一些共性内容。

        比如:不正常情况的信息,引发原因等。

        那么它们有了共性会向上抽取,抽取完了之后就会形成一个基本的体系,抽取出来的这个父类就叫做Throwable,Throwable下面就两个子类,一个叫Error,一个叫Exception,而这两个类的下面会有很多的子类出现。(就像感冒发烧一样,也会划分为不同类型的感冒)

        我们来看一下Java的API文档,看看它是怎么对这个体系进行介绍的。

        在java.lang中,有一个Throwable类。Throw是抛的意思,加上able就是可抛的意思。

        Throwable是Java中所有异常的父类(超类)。

        在这个类中,就定义了这个体系的共性内容。那么要使用一个体系,是不是就要看这个体系的父类父类的定义,建立子类的对象。

        来看一下这个类的定义。它继承了Object,实现了Serializable。它有两个直接子类,而我们要讲的就是Exception。

        先看Error:

        Error的小弟好多哦!而且我们发现,它的小弟名字最后都有Error。所以,Java在命名的时候,会把父类名称放在子类名字的最后,这个也是我们以后开发中可以借鉴的呢!而且这样命名,谁是它的父类,它是谁的子类也一目了然辣!

        我们再看下Exception:

        哇!它的小弟更多了耶。我们要把它们都学完吗?

        试着点进其中一个子类,发现:

        小弟竟然还有小弟,呜呜,学不完这么多呀。

        所以说我们把根本性的东西搞定就行了。

        Java把一些常见的问题都进行描述,并封装成异常了。

        这就是异常的基本的体系。

    06-面向对象(异常try-catch)

        接下来我们的程序已经发生异常啦!我们是不是可以用抛出异常来处理呢?

        就因为被除数为0,所以抛出了异常,我们需要处理一下。为什么呢?因为就因为这个小问题,导致后面的都不能运行了,哼!怎么办呢?

        在解决之前我们先了解一下这个抛出异常的过程。

        Java虚拟机,它认识这个叫ArithmeticException的异常,因为这是它定义的。引发了它所熟悉的状况,它就给你处理了。那是因为,Java虚拟机内部,有一个内置的异常处理机制。它把它熟悉的异常就给搞定了,但是搞定的方式很简单,就是只要程序出现问题,就不再往下运行。但是我们希望的是,把问题处理掉。

        这时候就涉及到第二部分,异常的处理。

        对于异常的处理,java为我们提供了特有的语句进行处理。

        try

        {

                需要被检测的代码;

        }

        catch(异常类 变量)

        {

                处理异常的代码;(处理方式)

        }

        finally

        {

                一定会执行的语句;

        }

        现在我们改写一下刚刚的代码,over就被打印啦。

        我们来说一下这个异常:

        有一个小问题,catch中,处理这个问题并没有用到传进来的对象e。

        想用呀?没问题~

        catch接收到异常后,相当于这个~

        这是不是多态呀?父类的引用指向子类的对象。

        想操作这个e该怎么办呢?当然是找方法。而异常对象的共性方法定义在Throwable当中。我们去Throwable当中看一下,都有什么方法呢?

        心好痛啊

        不管怎样,我都会好好努力的。

        我们发现,这里有个getMessage方法,可以获取信息,它返回的是一个字符串。

        对捕获到的异常对象进行常见方法操作:

        String getMessage();//获取异常的信息

        试一下:

        还有一个方法也可以:

        试一下:

        这个打印的就更全面啦,既有异常的信息,又有异常的名字。

        String toString();//打印:异常名称:异常信息

        还有这个方法:

        打印堆栈中的跟踪信息。注意,这个方法没有返回值,所以不要放到输出语句当中输出,它本身自己就可以打印了。

        试试:

        它是不是hin全面呀?既有异常名称、异常信息,同时又有异常的位置。

        void printStackTrace();//打印:异常名称,异常信息,异常出现的位置。

        其实jvm默认的异常处理机制,就是在调用printStackTrace方法,打印异常的堆栈的跟踪信息。

    07-面向对象(异常声明throws)

        接下来问题来了,这个功能是别人编写的,你在使用的时候,一定要做try处理吗?不一定。为什么呢?因为你根本不知道这个功能会不会发生问题。所以,这个问题你可处理可不处理,这样会导致一个现象,你在传参数的时候这个程序可能会停掉。

        所以这样开发是不OK的,怎么做是OK的呢?

        作为编写Demo的人,在编写了除法方法的时候,就要考虑到有可能会出现传进来的被除数为0的情况,这个时候就要在方法上做一个标识,告诉调用这个方法的人,“这个方法可能会出现问题哦”。这个标识就是throws。

        标识完会出现什么现象呢?

        我们试一下:

        对方声明了一个功能:这有可能发生问题。而这个问题,它又解决不了,它就把问题告诉给调用者。

        而这个时候,作为调用者,对方已经告诉你有可能发生问题,这个时候你需要做的就是:处理。

        如果你不处理,就会编译失败,不处理它就不让你用哦。它也是为了提高安全性。

        那该怎么处理呢?

        我们再来读一下这个报错:

        我们处理问题的方式有两种,要么捕捉它,要么抛出去。

        处理方式1:

        我也不管!把它抛出去!

        这是抛给谁了呢?抛给了虚拟机。结果是这样的:

        所以问题还是没有解决。

        一般情况下,我们不抛,而是捕捉一下。(后面还会讲什么时候抛,什么时候try,这是有区别的。)

        酱紫来写,运行一下,OK的喔:

        来一个传了错值的:

        用一个例子比较一下抛出和捕捉这两种方式的不同:

        有一个面包店,它家有一块面包放了三天都没有卖出去,这个时候这块面包就有可能会坏掉了。所以良心的店主给上面贴了个标签:放置三天后可能会坏。然后将它降价处理,从3块钱降到3毛钱。有个人就把它买走啦。

        抛出:回家之后一想,这个有可能坏掉,我也不知道该怎么解决,就把它给别人吧,有可能这个人也会给别人,但总会有个终点,就相当于虚拟机。都别吃啦!

        捕捉:把它放在微波炉里加热一下(catch),然后吃掉(最后正常执行的println(“over”);)。

        有点不太恰当~

    08-面向对象(多异常处理)

        我们在定义功能的时候,有可能会发生不止一个问题,我们在函数上声明异常的时候,声明的也就不只一个了。像刚刚声明的异常throws Exception,这个方式其实不是很好。因为它太广义啦,这里的问题是除法运算中可能会出现被除数为0的情况,如果声明得再具体一些,我们就能处理得更具体了。

        这个才是我们一般处理的方法。

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

        比如在刚刚那个例子中,我们就可以抛出算数异常:

        再多写一些语句,来看一下多种异常的情况:

抛出算数异常和数组角标异常

        调用时的处理:

        注意声明了几个异常,调用的时候就要写几个catch语句哦。

        没有出现异常的情况:

        出现算数异常的情况:

        出现数组角标异常的情况:

        那会不会两种异常同时发生呢?不会。因为当出现一个异常之后,这个异常就被抛出了,后面的语句就不会执行了哦。

        我们不写这么多catch,也一个catch可以吗? 

        这样来写:

        它存在的原因是多态性。不管抛什么异常它都可以处理。但是它处理没有针对性哦。

        所以最好写上有针对性的处理,每种异常要有与之匹配的catch。所以一般声明了几个抛出异常,调用时就应该写几个catch。

        这时候又有个问题,调用者担心万一发生的问题是那两个之外的,又写了一个catch语句:

        这样写也不是不可以,但是它有一个问题,就是处理的不具体。也就是说,你都不知道具体发生了,就把这个问题干掉了,相当于把这个问题隐藏了。而程序还在继续运行,也就是说,程序不知道发生什么事情了,继续在运行当中。为了安全性考虑,如果这个时候发生了对方指定的问题以外的问题,这个时候最好的处理方式就是:程序停掉。因为我们得知道,到底哪里卡住了,哪里出问题了,哪里是需要修正的。不能莫名其妙的把问题处理掉,发生了什么都不清楚。

        所以这个时候依旧写两个catch,当发生第三种问题的时候,我们就是要让程序停下来,来寻找这第三个问题到底是什么,把它标识出来,声明出来,调用的时候也可以提出具体的更有针对性的解决方法。

        注意,还有一种情况:

        运行之后会这样:

        第一个catch是大哥,后面两个是小弟,发生什么问题,大哥都能处理。因为catch是按顺序执行,而第一个catch都能处理,所以后面两个catch的存在,跟废话一样,永远执行不到。所以就报错了。

        综上:

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

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

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

        不要简单定义一句e.printStackTrace(),也不要简单的就书写一条输出语句。(刚刚那样写只是在演示,正式编写中不是这样简单处理的哦)因为打印它没有意义,用户看到了又能怎样呢?真正发生问题之后,一般会用一个文档把这些异常信息记录下来,记录完之后,我们把它称为异常日志文件。这样,在我们运行过程中,每天都会产生这些文件,记录了我们程序每天的运行状况:在什么时候几点几分,发生了一个什么问题。管理人员/网站的维护人员,就会经常去看这个日志,看完之后就会知道什么时候在代码的什么位置发生了什么问题,然后去把它搞定一下,看看为什么会产生这个问题,这个问题是不是大问题。所以,用文件记录下来,这是靠谱的。

   

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.shuli.cc/?p=12834,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?