JAVA学习day04

前言

昨日休整一天,今日如梦初醒甚是惭愧,下定决心努力学习,奋勇向前!

一、多态的理解

理解

多态解释视频

目前理解为多态主要靠重载和重写实现。

  • 重载:在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

重载就是多态!!

  • 重写:子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

Person s1 = new Student();当s1调用方法如s1.run()时进行以下步骤:

  1. 看父类有没有run()方法,如果没有则报错(编译看左,即编译看父类)
  2. 父类有run()方法,且子类没有重写run()方法,此时调用父类的run()方法
  3. 父类有run()方法,且子类重写run()方法,此时调用子类的run()方法(运行看右,即运行看子类是否重写)
  4. 如果父类没有run()方法,而子类有run()方法,则可以通过强制类型转换将s1转换为Student类然后才能执行子类方法。

代码

Fu

public class Fu {
    public void fun1(){
        System.out.println("Fu fun1");
    }
    public void fun2(){
        System.out.println("Fu fun2");
    }
}

Zi

public class Zi extends Fu{
    @Override
    public void fun1() {
        System.out.println("Zi fun1");
    }
    public void fun3(){
        System.out.println("Zi fun3");
    }
}

Application

public class Application {
    public static void main(String[] args) {
        //调用方法run()
        Fu obj = new Zi();
        obj.fun1();
        obj.fun2();
        ((Zi)obj).fun3();
    }
}

运行结果:
Zi fun1
Fu fun2
Zi fun3
(无法直接运行obj.fun3()必须强制转类型)

另:三种方法不可被重写

  • static 方法,属于类,不可被重写
  • final 方法,常量
  • private 方法,私有

二、instanceof的用法

用法介绍

公式

instanceof (类型转换)引用类型,判断一个对象是什么类型。

A a = new B();
a instanceof X;

A是父类引用,B是子类对象;

比较流程

  1. 判断A(引用类型,即父类)和X的关系,当X为A的子类时才可进行下一步,否则编译阶段即报错。
  2. 判断B和X的关系,如果X是B的父亲或本身,则返回true;否则,如果X是B的子类或同级则返回false;

代码

Person

public class Person {
    public void run(){
        System.out.println("run");
    }
}

Student

public class Student extends Person{
    public void go(){
        System.out.println("son go");
    }
}

Applocation1

public class Application1 {
    public static void main(String[] args) {
        //关系:        
        //Object>String
        //Object>Person>Student
        //Object>Person>Teacher
        
        //引用类型为Object时(祖宗类)
        Object object = new Student();

        System.out.println(object instanceof Student);
        System.out.println(object instanceof Person);
        System.out.println(object instanceof Object);
        System.out.println(object instanceof Teacher);
        System.out.println(object instanceof String);

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

        
        //引用类型为Person时
        Person person = new Student();

        System.out.println(person instanceof Student);
        System.out.println(person instanceof Person);
        System.out.println(person instanceof Object);
        System.out.println(person instanceof Teacher);
        //System.out.println(person instanceof String);//编译即报错

        System.out.println("=============");
        
        
        //引用类型为Student时(祖宗类)
        Student student = new Student();

        System.out.println(student instanceof Student);
        System.out.println(student instanceof Person);
        System.out.println(student instanceof Object);
        //System.out.println(student instanceof Teacher);//编译即报错
        //System.out.println(student instanceof String);//编译即报错

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

        //引用类型为Person指向对象为Person型时
        Person person2 = new Person();
        System.out.println(person2 instanceof Student);
        System.out.println(person2 instanceof Person);
        System.out.println(person2 instanceof Object);
        System.out.println(person2 instanceof Teacher);
    }
}

运行结果:

true
true
true
false
false
============
true
true
true
false
============
true
true
true
============
false
true
true
false

三、类型转换

规律

  1. 父引用指向子对象
  2. 子->父,向上转型
  3. 父->子,向下转型:强转
  4. 方便方法调用,减少重复代码

代码

父类和子类代码同上

Application2

public class Application2 {
    public static void main(String[] args) {
        
        //类型转换:基本类型转换 高低64 32 16 8

        Person obj = new Student();
        //obj.go();不能用go方法
        //需要将obj转成Student类型
        Student student = (Student) obj;
        student.go();
        //或者((Student) obj).go;

        //子转父可能会丢失子方法(向上转型)
        Person person = student;
        //student转person是低转高,默认转换即可,但可能会丢失方法
        //person.go();
    }
}

运行结果
son go

四、static详解

  • 非静态的方法可以随意调用静态方法;静态方法只能调用静态方法

代码块

public class Student {
    {
        System.out.println("匿名代码块");
    }

    static {
        System.out.println("静态代码块");
    }
    public Student(){
        System.out.println("构造方法");
    }

    public static void main(String[] args) {
        Student s1 = new Student();
        System.out.println("_________");
        Student s2 = new Student();

    }
}

运行结果:
静态代码块
匿名代码块
构造方法
_________
匿名代码块
构造方法

注意

  • 静态代码块只执行一次
  • 匿名代码块在构造方法前可以用来赋初始值

对一些想法的测试

如果不构造本类会发生什么呢?

1. 测试多态

public class Student {
    {
        System.out.println("匿名代码块");
    }

    static {
        System.out.println("静态代码块");
    }
    public Student(){
        System.out.println("构造方法");
    }

    public static void main(String[] args) {
        Person s3 = Student();
    }
}

结果:
静态代码块
匿名代码块
构造方法

2. 测试不构造本类

public class Student {
    {
        System.out.println("匿名代码块");
    }

    static {
        System.out.println("静态代码块");
    }
    public Student(){
        System.out.println("构造方法");
    }

    public static void main(String[] args) {
        Person s4 = Person();
    }
}

结果:
静态代码块

总结:

  1. 多态使用子类的构造器
  2. static和类一起编译运行,只要加载此类必然且只运行一次

静态导入别的类的静态方法

import static java.lang.Math.random;

public class Test{
    public static void main(String[] args) {
        System.out.println(random());
        //通过静态导入方法,即无需再打Math.random(),只打random()即可;
    }
}

五、抽象类

abstract

  • 抽象类中可以有抽象方法和普通方法
  • 不能new抽象类,只能依靠子类去实现
  • 子类必须重写new方法
  • 抽象方法必须在抽象类中
  • 抽象类有构造方法(用来给子类提供,所有子类必须调用从Object开始的所有父类的构造函数)
  • 抽象类可以写静态方法
  • 提高开发效率

六、接口

普通:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口(Interface):一组规范,不可以写方法体和静态方法,没有构造器
接口本质是契约

接口的所有定义都是抽象的public abstract
接口的所有常量都是public static final

代码

UserService(interface)

public interface UserService {

    //常量 public static final
    int AGE = 99;

    //接口中所有定义的方法都是抽象的public abstract
    void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
}

TimeService(interface)

public interface TimeService {
    void timer();
}

UserSeriviceImp1

//实现接口的类,必须重写接口中的方法
public class UserServiceImp1 implements UserService,TimeService{
    @Override
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void update(String name) {

    }

    @Override
    public void query(String name) {

    }

    @Override
    public void timer() {

    }
}

七、内部类

类的内部再定义一个类

代码

Outer

public class Outer {
    private int id=10;
    public void out(){
        System.out.println("这是外部类的方法");
    }

    //如果Inner被static修饰则不能getID,因为static直接加载,除非把id也改成static
    class Inner{
        public void in(){
            System.out.println("这是内部类的方法");
        }

        //获得外部类的私有属性
        public void getID(){
            System.out.println(id);
        }
    }

    //局部内部类
    public void method(){
        class Inner{
            public void in(){

            }
        }
    }
}

//一个java类中可以有多个class类,但只能有一个public class
class A{
    public static void main(String[] args) {
    }
}

Application

public class Application {
    public static void main(String[] args) {
        //new

        Outer outer = new Outer();
        //通过外部类来实例化内部类
        outer.new Inner();
        Outer.Inner inner = outer.new Inner();
        inner.in();
        inner.getID();
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //匿名对象的使用,没有名字初始化类,不用将实例保存到变量中
        new Apple().eat();

        UserService userService = new UserService() {

            @Override
            public void hello() {

            }
        };
    }
}
class Apple{
    public void eat(){
        System.out.println("1");
    }
}

interface UserService{
    void hello();
}

八、异常

什么是异常

Exception,程序运行出现了异常;
异常:程序运行中出现了不期而至的状况

分类

  • 检查性异常:代表性的例子是用户错误或问题引起的异常。程序员无法预见,如打开不存在的文件,就会发生一个异常。
  • 运行时异常:可能被程序员避免的异常。运行时异常可以在编译时被忽略
  • 错误ERROR:错误不是异常是脱离程序员控制的问题。例如栈溢出,它们在编译时也检查不到。

异常分类

异常体系结构

  • Java把异常当作对象处理,定义java.lang.Throwable作为所有异常的超类
  • Java API中已经定义了许多异常类,分为两大类,错误Error和异常Exception。

具体介绍

Error

  • Error类对象由Java虚拟机抛出,大多数错误和代码编写者的操作无关。
  • Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,会出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
  • 还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError),链接错误(LinkageError)。这些错误时不可查的,因为它们在应用程序的控制和处理能力之外,而且绝大多数时程序运行时不允许出现的情况。

Exception

  • Exception分支中有一个非常重要的子类RuntimeException(运行时异常)
    • ArrayIndexOutOfBoundsException(数组下标越界)
    • NullPointerException(空指针异常)
    • ArithmeticException(算数异常)
    • MissingResourceException(丢失资源)
    • ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获异常,也可以选择不处理。
  • 这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
  • Error和Exception的区别:Error通常是灾难性的致命错误,是程序无法控制和处理的,当出现这些异常是,Java虚拟机(JVM)会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能地去处理这些异常。

处理机制

异常处理的五个关键字:try、catch、finally、throw、throws

代码

Test

public class Test {
//假设捕获多个异常,从小到大!
        try {//try监控区域
            if (b==0){
                throw new ArithmeticException();//主动抛出异常
            }
            System.out.println(a/b);
            //new Test().a();
        }catch(Error e){
            System.out.println("Error");
        }
        catch (ArithmeticException e){//catch(想要捕获的参数类型) 捕获异常
            System.out.println("Exception");
        }catch (Throwable e){
            System.out.println("Throwable");
        }finally{
            //finally一定会被执行,善后工作
            System.out.println("finally");
        }

        //finally可以没有,但try和catch必须要有
        //如果是IO异常,需要关闭,来释放资源
}

第一部分总结:

  • try后面可以跟多个catch,catch应该由小到大
  • finally代码块是一定会被执行的,一般负责善后工作,如:关闭IO流

Test

public class Test {
    public static void main(String[] args) {
        try {
            new Test().test(1,0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } 
    }

    //假设这个方法中,处理不了这个异常,方法上抛出异常
    public void test(int a,int b) throws ArithmeticException{
        if (b==0){
            throw new ArithmeticException();//主动抛出异常
        }
    }
}

第二部分总结

  • throws用在方法上,throw用在try catch内;如果有方法里解决不了的异常,就由throws,抛至上层进行处理

Test2

public class Test2 {
    public static void main(String[] args) {
        int a=1;
        int b=0;


        //ctrl+alt+t 快速开启环绕菜单
        //QQ占用热键,关闭QQ或用ctrl+alt+win+t可以开启
        try {
            System.out.println(a/b);
        } catch (Exception e) {
            e.printStackTrace();//打印错误的栈信息
        }finally {

        }
    }
}

第三部分总结

臭QQ搞得我快捷键搞半天,ctrl+alt+t可开启环绕菜单,热键被占多按个win

自定义异常

用户可以自定义异常,只需继承Exception类。

步骤:

  1. 创建自定义异常类。
  2. 在方法中通过throw关键字抛出异常对象
  3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并进行处理;否则在方法的声明中通过throws关键字知名要抛出给方法调用者的异常,继续进行下一步操作。
  4. 在出现异常方法的调用者中捕获并处理异常

代码

MyException(自定义的异常)

//自定义的异常类
public class MyException extends Exception{

    //传递数字>10

    private int detail1;

    public MyException(int a) {
        this.detail1 = a;
    }
    //toString异常的打印信息
    @Override
    public String toString() {
        return "MyException{" +
                "detail1=" + detail1 +
                '}';
    }
}
  • toString就是将实例化对象转换成能输出的字符串类型,自动转换可以sout直接输出

TestMyException测试类

public class TestMyException {

    //可能存在异常的方法

    static void test(int a) throws MyException{
        System.out.println("传递的参数为"+a);
        if(a>10){
            throw new MyException(a);//抛出
        }
        System.out.println("ok");
    }

    public static void main(String[] args) {
        try {
            test(11);
        } catch (MyException e) {
            System.out.println("MyException=>"+e);
        }
    }
}

完成对异常的测试,test成功捕获后抛出给主函数,主函数catch捕获到MyException后执行操作,将捕获到的异常实例e自动通过toString方法,通过sout输出。

异常经验总结

  • 处理运行异常时,采用逻辑去合理规避同时辅助try-catch处理
  • 在多重catch块后,可以加一个catch(Exception)来处理可能被遗漏的异常
  • 对于不确定的代码,也可以加上try-catch,处理潜在的异常
  • 尽量处理异常,切忌只是调用printStackTrace()来打印输出
  • 具体如何处理,根据不同业务和异常类型去决定
  • 尽量添加finally语句去释放占用资源
posted @ 2021-12-08 19:26  计算机181胡佛  阅读(59)  评论(0编辑  收藏  举报