java基础第三篇

目录

多态:

成员内部类: 

局部类部类

匿名内部类:

接口实现关系下的匿名内部类

异常处理:

具体说说异常的体系:


多态:

         1. 多态用于形参类型的时候,可以接收更多类型的数据 。

         2. 多态用于返回值类型的时候,可以返回更多类型的数据。

        多态的好处: 提高了代码的拓展性。

        需求1: 定义一个函数可以接收任意类型的图形对象,并且打印图形面积与周长。

        MyShape.java

//图形类

abstract class MyShape{

public abstract void getArea();

public abstract void getLength();

}

        做一个抽象图形类,然后让子类去继承,这个子类就是圆类,内部可以有求面积方法,与求周长的方法

        Circle.java

        

        然后我们在定义一个矩形类,里面也有求面积与周长的方法

        Rect.java

        

        然后下面是主类的方去实现.

        

        

        上面说了多态的一种情况,就是父类应用类型变量指向了子类的对象

        下面来说一下,接口引用类型变量,指向了接口实现类的对象

        先来看一个接口

        Demo5.java

package pxx.test;

interface Demo5{
    int add(int num1,int num2);
    void say();
}

        再来看它的实现类

        Demo6.java

package pxx.test;

public class Demo6 implements Demo5{
    @Override
    public int add(int num1, int num2) {
        return num1 + num2;
    }

    @Override
    public void say() {
        System.out.println("我是Demo5");
    }
}

        再来看主类

        Demo7.java

package pxx.test;

public class Demo7 {
    public static void main(String[] args) {
        //接口引用类型变量指向接口实现类对象
        //普通方法全是调用子类的方法
        Demo5 a = new Demo6();
        int res = a.add(1,2);
        a.say();
        System.out.println(res);

    }
}

         运行结果:

        

成员内部类: 

        直接在一个类的内部,把另外一个类当成成员变量、方法一样对待就可以了

        比如

                下面说一下成员内部类几个需要注意的地方:

                1.在内部类中,外部类与内部类存在同名成员变量时,默认情况是访问内部类成员变量如果我们想指定外部类的成员变量:Outer.this.外部成员变量 this前面代表this是哪一个对象来调用的。

说一句比较重要的话:非静态内部类隐含地持有对外部类实例的引用。因此,可以通过这个引用来访问外部类的非静态成员。反之,静态内部类不隐含地持有对外部类实例的引用。因此,不能直接访问外部类的非静态成员(只能直接访问静态成员)。

                2.既然内部类是成员,自然就可以用public,private等来修饰,如果设置成private,那么就不能再其他类直接创建对象。只能通过内部打开的接口来调用者使用

                3.成员内部类一旦出现静态成员,该类也要用static来修饰

上面是不是说明了普通类需要一个外部类的实例存在才能创建,我们可以按照下面的方式来进行创建

package com.pxx.test;  
  
public class Test2 {  
    private int outerField = 42;  
  
    public class InnerClass {  
        public void displayOuterField() {  
            System.out.println("Outer Field from Inner Class: " + Test2.this.outerField);  
        }  
    }  
  
    public Test2() {  
        // 提供一个构造方法,这样就可以在main方法中创建Test2的实例,从而可以创建InnerClass的实例  
    }  
  
    public static void main(String[] args) {  
        Test2 test2 = new Test2(); // 创建外部类实例  
        InnerClass innerClass = test2.new InnerClass(); // 通过外部类实例创建内部类实例  
        innerClass.displayOuterField(); // 输出:Outer Field from Inner Class: 42  
    }  
}

 我们要在另外一个类里面去访问外部类的内部类,该如何访问

 

                Demo4.java

package pxx.test;

public class Demo4 {
    public static void main(String[] args) {
        System.out.println(Outer1.Inner1.i);
    }
}

class Outer1 {
    int x = 100;

    int i = 1000;

    //成员内部类
    static class Inner1 {
        static int i = 0;

        public void print() {
            System.out.println("i = " + i);//同名成员先访问内部
        }
    }
}

        我们看上面的代码:

        我们都知道静态成员是先于对象存在。ok,那我们想一下,如果内部类定义了一个static成员,那么这个成员的访问方式就可new Outer().Inner.成员。(也就是类名.成员)这个怎么去访问,这样是访问不了的

        但是此时,如果外部类对象不存在,没有实例化,那么内部类成员也就不存在,内部类不存在,那怎么去调用内部类的静态成员。(注意class加载不一定创建了对象)

        问:成员内部类,如果是静态的怎么访问。静态函数,不能调用非静态函数,非静态成员变量,不能带this,因为这些都是基于对象存在。那么如果这个类是一个静态的呢?

        普通类不能是静态的

局部类部类

        (在函数内部写一个类):

        局部内部类与局部变量一样,只能在方法类中去使用

        

        我们可以直接在方法内部实例化一个对象,直接使用,直接调用(也是唯一的创建使用放法)

        上面y是一个局部变量,局部内部类,访问一个局部变量,该变量必须用final修饰,问什么?

分析一下y什么时候从内存消失:方法执行完毕

        Inner的对象什么时候从内存中消失:不在引用,等待垃圾回收机制回收,它才会消失。上面说明了,Inner的生命周期比局部变量y长,而且当y消失了,Inner还在访问y,这样感觉y的周期被延长了,违背设计原则,造一个复制品,那么在这个过程中,就不允许修改,所以把一个局部变量定义成final,final的意思是不能修改。

匿名内部类:

        没有类名的类,就是匿名内部类,准确说是没有引用变量指向的类。

        简化书写

        使用前提:必须存在继承或者实现关系才能使用

        匿名内部类只是没有类名,其他什么完全具备,所以借助父类或者接口的名字:

        

        

        上面借助了父类的类名来做一个匿名内部类

        

        上面就是匿名内部类的调用方式

        

上面这样没有引用类型指向的匿名内部类,是可以定义自己独特的方法的。

        

        上面就是调用完一个方法的同时,返回一个对象在调用下一个方法

        ​​​​​​ 

        上面就是可以引用变量名来调用方法

接口实现关系下的匿名内部类

        匿名内部类:一般用于实参,我们一定要考虑实际内存开销

        先来看一个接口:

        Dao.java

package pxx.test1;

public interface Dao {
    void add();
}

        主类Demo1.java

        

package pxx.test1;

class Demo1 {

    public static void main(String[] args) {

        test(new Dao() {
            public void add() {
                System.out.println("添加员工成功");
            }
        });

    }

    //调用这个方法...
    public static void test(Dao d) {
        //形参类型是一个接口引用..带名字的匿名内部类对象
        d.add();
    }
}

         运行结果:

         

异常处理:

        说白了,什么错误,该不该去捕获这些错误,以及如何判断错误等级,最后如何处理错误。

        JAVA有一个健全的异常体系,有很多异常类,描述了java开发过程中不正常的情况。

        

        

        我们可以看到下面的子类。

        

        Throwable是所有异常类的一个父类,我们重点学父类的方法,因为父类的方法子类也有,完了,我们在掌握子类特有的方法

        下面有两个子类:错误Error和异常Exception

        

        Error:错误的话情节比较严重

        Exception:分为运行时异常(不一定处理)与编译时异常(一定要去处理),但我建议最好都处理

        异常体系:

                --------| Throwable

                ------------| Error

                ------------| Exception

        Throwable常用的方法:

                toString()  返回当前异常对象的完整类名+病态信息。比getMessage()处理信息更高级

                getMessage() 返回的是创建Throwable传入的字符串信息。这个信息我们可以自己定义

                printStackTrace() 打印异常的栈信息。

.          

        

        

        

        

        

        上面是一小部分,展示的是printStackTrace的用法。生病生在哪里的一系列追踪36行-》30行出问题

        

具体说说异常的体系:

        ----------| Throwable  所有异常或者错误类的超类

        --------------|Error  错误   错误一般是用于jvm或者是硬件引发的问题,所以我们一般不会通过代码去处理错误的。

        --------------|Exception 异常   是需要通过代码去处理的。

        如何区分错误与异常呢:

                如果程序出现了不正常的信息,如果不正常的信息的类名是以Error结尾的,那么肯定是一个错误。

                如果是以Exception结尾的,那么肯定就是一个异常。

                

                上面就是一个错误,这个错误的原因是,我们没有去找到这个文件,这个情况出现的比较硬,不属于代码可以解决的范围

                Demo13.java

package pxx.test1;

class Demo13 {

    public static void main(String[] args) {

        //java虚拟机在默认的情况下只能管理64m内存。
        //这里太大可能就会造成溢出
        byte[] buf = new byte[1024 * 1024 * 1024];

        System.out.println("Hello World!");

    }
}

  下面就会报出一个内存溢出错误

        但是像上面的错误,我们避免不了,这个是由于虚拟机与硬件环境造成的,与我们没有关系

错误我们尽力去避免,但是出现异常,就必须解决异常,要么捕获,要么进行抛出,特别是编译异常。        

        

        上面函数调用部分会出现一个算术异常,违背现实常理

        

        

        疑问: 下面的信息是通过printStackTrace方法打印出来,那么异常对象从何而来呢?

        Exception in thread "main" java.lang.ArithmeticException: / by zero

        at Demo10.div(Demo10.java:10)

        at Demo10.main(Demo10.java:5)

        jvm运行到a/b这个语句的时候,发现b为0,除数为0在我们现实生活中是属于不正常的情况,jvm一旦发现了这种不正常的情况时候,那么jvm就会马上创建一个对应的异常对象,并且会调用这个异常对象 的printStackTrace的方法来处理。

        我们应该对于用户输入的数据进行监控,有异常抛出。

        异常的处理:

                 方式一:捕获处理

                        捕获处理的格式:

                        try{

                        可能发生异常的代码;

                }catch(捕获的异常类型 变量名(随便起,这个就是jvm虚拟机创建的一个对象引用变量)){

                        处理异常的代码....

        }

                方式二:抛出处理

                       如果我们不知道捕获的是什么异常,就用Exception来替代,异常处理之后,程序就会接着往下面继续执行。

        Demo3.java

package pxx.test1;

class Demo3 {

    public static void main(String[] args) {
        int[] arr = null;
        div(4, 0, arr);

    }

    public static void div(int a, int b, int[] arr) {

        int c = 0;

        try {

            c = a / b;//jvm在这句话的时候发现了不正常的情况,那么就会创建一个对应的异常对象。

            System.out.println("数组的长度:" + arr.length);

        } catch (ArithmeticException e) {

            //处理异常的代码....

            System.out.println("异常处理了....");

            System.out.println("toString:" + e.toString());

        } catch (NullPointerException e) {

            System.out.println("出现了空指针异常....");

        } catch (Exception e) {

            System.out.println("我是急诊室,包治百病!");

        }

        System.out.println("c=" + c);

    }

}

        上面运行结果:

                 

        捕获处理要注意的细节:

                1. 如果try块中代码出了异常经过了处理之后,那么try-catch块外面的代码可以正常执行。

                2. 如果try块中出了异常的代码,那么在try块中出现异常代码后面的代码是不会执行了。

那么异常代码块之外的代码是会正常执行的。

                3. 一个try块后面是可以跟有多个catch块的,也就是一个try块可以捕获多种异常的类型。

                4. 一个try块可以捕获多种异常的类型,但是捕获的异常类型必须从小到大进行捕获,否则编译报错。

        下面说一下抛出处理方法:

                把异常的信息比如服务器本身的问题发送给服务器,服务端解决问题,发布新版本,但是有一些不该发送到服务器的数据,那么我们就来做一下其他处理,比如用户在下载过程中没有wifi了

                抛出处理(throw、throws)

                抛出,抛给谁?服务器?用户?。。。

                1.如果一个方法的内部抛出了一个异常对象,那么必须要在方法上声明抛出。

                

                2.如果调用了一个声明抛出异常的方法,调用者必须处理异常,在调用的地方处理异常

                

                在什么情况下抛出异常,上面是在b==0的情况下,当然上面我们选择了异常捕获,我们还可以选择在此抛出,不处理,谁调用main?抛给jvm

                3.一个方法抛出一个异常之后,那么抛出语句后面的语句就会停止执行,还有就是一个方法内部遇到throw关键字也会马上停止执行。

        ​​​​​​​        

                4.在函数内部,我们一次只能抛出一种异常对象,当然在函数声明上 可以抛出多个异常类型

                下面说一下throw与throws区别:

                        throw用于内部,throws一个用于方法上面

                        throw关键字是用于方法内部抛出一个异常对象的,throws关键字是用于在方法声明上声明抛出异常类型的。

                        throw关键字后面只能有一个异常对象,throws后面一次可以声明抛出多种类型的 异常。

                Demo4.java

                

package pxx.test1;

class Demo4 {

    public static void main(String[] args) {

        try {

            int[] arr = null;

            div(4, 0, arr); //这里会是算术异常,但是值catch一个Exception,就会走这条路

            System.out.println("前面异常处理完了,我运行了");

        } catch (Exception e) {

            System.out.println("出现异常了...");

            e.printStackTrace();

        }

    }

    public static void div(int a, int b, int[] arr) throws Exception, NullPointerException {

        if (b == 0) {

            throw new Exception(); //抛出一个异常对象...

        } else if (arr == null) {

            throw new NullPointerException();

        }

        int c = a / b;

        System.out.println("c=" + c);

    }

}

                运行结果:

                          

                疑问:何时使用抛出处理?何时捕获处理?原则是如何?

                如果你需要通知到调用者,你代码出了问题,那么这时候就使用抛出处理.,如果代码是直接与用户打交道遇到了异常千万不要再抛,再抛的话,就给了用户了,这时候就应该使用捕获处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值