08.封装继承多态

封装

  • 该露的露,该藏的藏

    • 我们程序设计要追求 “高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅宝路少量的方法给外部使用。
  • 封装(数据的隐藏)

    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏
  • 记住这句话:属性私有,get/set

  • package OOP.Test10;
    
    /*
        封装:
            1.提高程序的安全性,保护数据
            2.隐藏代码的实现细节
            3.统一接口
            4.系统的可维护性增加了
     */
    public class Application {
        public static void main(String[] args) {
            Student lxk = new Student();
            lxk.setName("李小康");
            System.out.println(lxk.getName());
            lxk.setAge(999);
            System.out.println(lxk.getAge());
        }
    }
    
  • package OOP.Test10;
    
    public class Student {
         // private 私有
        private String name;// 学生姓名
        private int age; // 学生年龄
        private char sex; // 学生性别
        private int id; // 学生号
    
        // 提供一些操作这些属性的方法
        // 即提供一些public的get,set的方法
        // get()即获得这个属性,set()设定属性的值
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    //  Alt+ Insert 自动生成
        public int getAge() {
            if (age>130||age<0){
                age = 3;
            }else {
                this.age = age;
            }
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public char getSex() {
            return sex;
        }
        public void setSex(char sex) {
            this.sex = sex;
        }
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
        void study(){
        }
    }
    

继承

  • 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。

  • extends的意思是“扩展”。子类是父类的扩展。

  • JAVA中类只有单继承,没有多继承!一个字类只能有一个父类,而一个父类可以有多个子类

  • 继承是类与类之间的一种关系。除此之外,类和类之间的关系还有依赖,组合,聚合等。

  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用extends关键字来表示。

  • 子类和父类之间,从意义上讲应该具有“is a”的关系。

  • object(所有类的父类)

  • package OOP.Test11;
    
    public class Application {
        public static void main(String[] args) {
            Student student = new Student();
            student.say(); // 输出结果:说了一句话
            System.out.println(student.money); // 输出结果:1000000000
        }
    }
    
  • package OOP.Test11;
    
    import java.util.Scanner;
    
    // 父类
    public class Persion /*extends Object*/ {
        public int money = 10_0000_0000;
        public  void say(){
            System.out.println("说了一句话");
        }
        public int getMoney(){
            return money;
        }
        public void setMoney(int money){
            this.money = money;
        }
    }
    
  • package OOP.Test11;
    
    // 子类(派生类)  学生 is 人
    // 子类继承了父类,就会拥有父类的全部方法!
    public class Student extends Persion {
        // 快捷键 Ctrl+H  : 打开继承树
    
    }
    
  • super

    • 注意点:
          1.super调用父类的构造方法,必须在构造方法的第一个
          2.super必须只能出现在子类的方法或者构造方法中!
          3.super和this不能同时调用构造方法!
      与this的不同点:
          代表对象不同:
              this:本身调用者这个对象
              super:代表父类对象的引用
          构造方法
              this(); // 本类的构造
              super(); // 父类的构造
      
  • package OOP.Test12;
    
    public class Application {
        public static void main(String[] args) {
            Student student = new Student();
    //        student.test("李小康");
    //        student.test1();
            // 先执行父类的无参构造后执行子类的无参构造
        }
    }
    
  • package OOP.Test12;
    
    public class Persion {
        protected String name = "lixiaokang";
    /*
        // 私有的东西无法被继承
        prative void print(){
            System.out.println("Persion");
        }
     */
        public Persion() {
            System.out.println("Persion类的无参构造执行了!");
        }
    
        public void print(){
            System.out.println("Persion");
        }
    }
    
  • package OOP.Test12;
    
    
    public class Student extends Persion{
        private String name = "lxk";
        public void test(String name){
            System.out.println(name); // 输出结果:李小康
            System.out.println(this.name); // lxk
            System.out.println(super.name); // 输出结果: lixiaokang
        }
    
        public Student() {
            // 隐藏代码,调用了父类的无参构造
            //        super();// 调用父类的构造器,必须位于子类构造器的第一行
            System.out.println("Student类的无参构造执行了!");
        }
    
        public void print(){
            System.out.println("Student");
        }
        public void test1(){
            print(); // 输出结果:Student
            this.print(); // 输出结果: Student
            super.print(); // 输出结果: Persion
    
        }
    }
    
  • 方法重写

    • package OOP.Test13;
      
      public class Application {
          // 静态方法和非静态方法的区别很大
          public static void main(String[] args) {
      
             // 静态方法: // 方法的调用只和左边调用的数据类型有关
              // 非静态方法时且非私有方法时:重写
      
              A a = new A();
      //        a.test(); // 输出结果为:A-->test
              a.test(); //输出结果为:A-->test
              // 父类的引用指向子类()
              B b = new A();
      //        b.test(); // 输出结果为:B-->test()
              b.test(); // 输出结果为:A-->test // 子类重写了父类的方法
          }
      }
      
    • package OOP.Test13;
      
      public class A extends B{
      //    public static void test(){
      //        System.out.println("A-->test()");
      //    }
          public void test(){
              System.out.println("A-->test()");
          }
      }
      
    • package OOP.Test13;
      
      // 重写,都是方法的重写,与属性无关。
      public class B {
      //    public static void test(){
      //        System.out.println("B-->test()");
      //    }
          public void test(){
              System.out.println("B-->test()");
          }
      }
      
    • 重写总结:
          重写:需要有继承关系,子类重写父类的方法
              1.方法名必须相同
              2.参数列表必须相同
              3.修饰符:范围可以扩大:public>protected>Default>private
              4.抛出的异常:范围:可以被缩小,但不能扩大:
          重写,子类的方法和父类必须一致:方法体不同。
          
          什么情况下不能重写:
              1.static 方法,属于类,它不属于实例
              2.final 常量;
              3.private方法
              
      为什么需要重写:
          1.父类的功能,子类不一定需要,或者不一定需要满足
          快捷键:Alt+Insert
              
      

多态

  • 即同一方法可以根据发送对象的不同而采用多种不同的行为方式。

  • 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多

  • 多态存在的条件

    • 有继承关系
    • 子类重写父类方法
    • 父类引用指向子类的对象
  • 注意:多态是方法的多态,属性没有多态性

  • package OOP.Test14;
    
    public class Application {
        public static void main(String[] args) {
            //一个对象的实际类型是确定的
            //new Student();
            //new Persion();
            //可以指向对象的引用类型就不确定了,父类的引用指向子类的类型
    
            // Student 能调用的方法都是自己的或者重父类调用的
            Student s1 = new Student();
            // Persion 父类型,可以指向子类,但不能调用子类独有的方法
            Persion s2 = new Student();
            Object s3 = new Student();
            // 对象能执行哪些方法,主要看对象左边的类型,和右边的关系不大
            s2.run(); // 子类重写了父类的方法,执行子类重写后的方法
            s1.run();
            s1.eat();
    //        s2.eat(); // 父类对象不能调用子类独有的方法
        }
    }
    
  • package OOP.Test14;
    
    public class Persion {
    
        public void run(){
            System.out.println("run!!");
    
        }
    }
    
  • package OOP.Test14;
    
    public class Student extends Persion {
        @Override
        public void run() {
            System.out.println("son!!");
        }
        public void eat(){
            System.out.println("eat!!");
        }
    }
    
  • 多态注意项:
        1.多态是方法的多态,属性没有多态
        2.父类和子类,有联系  注意类型转换异常(ClassCastException!)
        3.存在条件:继承关系,方法的重写,父类的引用指向子类对象!
        
    
  • instanceof(类型转换) 引用类型,判断一个对象是什么类型

    • package OOP.Test15;
      
      public class Application {
          public static void main(String[] args) {
              /*
              Object>String
              Object>Persion>Teacher
              Object>Persion>Student
               */
              Object object = new Student();
      
              // X x = new Y() ;
              // System.out.println(x instanceof Yn); // 当x和Y没有关系时不能编译通过,当Y和Yn没有关系时结果为false,其他情况下为true
      
              System.out.println(object instanceof Object); // true
              System.out.println(object instanceof Persion); // trye
              System.out.println(object instanceof Student); // true
              System.out.println(object instanceof Teacher); // false
              System.out.println(object instanceof String); // false
      
              System.out.println("================================");
      
              Persion persion = new Student();
              System.out.println(object instanceof Object); // true
              System.out.println(persion instanceof Persion); // trye
              System.out.println(persion instanceof Student); // true
              System.out.println(persion instanceof Teacher); // false
      //        System.out.println(persion instanceof String); // 编译错误,二者不能转换
      
              System.out.println("=================================");
      
              Student student = new Student();
              System.out.println(student instanceof Object); // true
              System.out.println(student instanceof Persion); // trye
              System.out.println(student instanceof Student); // true
      //        System.out.println(student instanceof Teacher); // 编译错误,二者不能转换
      //        System.out.println(student instanceof String); // 编译错误,二者不能转换
          }
      }
      
    • package OOP.Test15;
      
      public class Persion {
          public void run(){
              System.out.println("run");
          }
      }
      
    • package OOP.Test15;
      
      public class Student extends Persion {
          public void go(){
              System.out.println("go");
          }
      }
      
    • package OOP.Test15;
      
      public class Teacher extends Persion {
      }
      
  • 
    
  • package OOP.Test15;
    /*
    1.父类的引用指向子类的对象
    2.把子类转换为父类,自动转换,可能会导致自己原来的方法丢失
    3.把父类转换为子类需强制转换
    4.方便方法的调用,减少重复的代码
    抽象:继承 封装 多态     抽象类 接口
     */
    public class Application {
        public static void main(String[] args) {
    
    // 类 型 之 间 的 转 换 : 父    子
    //        高                     低
            Persion student = new Student();
    //        student.go();// 编译报错,Persion里面没有go()方法
    
            // 将student对象的Persion类型转换为Student类型,就可以使用Student类型的方法了
            Student student1 = (Student) student;
            student1.go();
    //       ((Student) student).go();// 此代码也可代替上面两行代码
            // 高               低
            Persion persion = student; // 低类型到高类型自动转换
    //        persion.go(); 编译错误,低类型到高类型会存在低类型中的方法丢失
    
        }
    }
    
  • package OOP.Test15;
    
    public class Persion {
        public void run(){
            System.out.println("run");
        }
    }
    
  • package OOP.Test15;
    
    public class Student extends Persion {
        public void go(){
            System.out.println("go");
        }
    }
    
  • package OOP.Test15;
    
    public class Teacher extends Persion {
    }
    
  • static关键字详解

    package OOP.Test16;
    
    public class Student {
        private static int age; //静态的变量
        private double score; // 非静态成员变量
    
        public void run(){
    
        }
        public static void go(){
    
        }
    
        public static void main(String[] args) {
    //        静态方法,可以通过类名直接调用。
            Student.go();// go() 也可以。
    //        非静态方法,必须类创建的对象来调用
            new Student().run();
    
            Student s1 = new Student();
    
            System.out.println(Student.age);
            //score 为非静态成员变量,不能通过类名直接调用。
    //        System.out.println(Student.score);// 编译错误
            System.out.println(s1.age);
            System.out.println(s1.score);
        }
    }
    
    • package OOP.Test16;
      
      public class Persion {
          // 2nd
          {
              System.out.println("匿名代码块");
          }
          // 1st
          static {
              System.out.println("静态代码块");
          }
          // 3rd
          public Persion() {
              System.out.println("构造方法");
          }
      
          public static void main(String[] args) {
              /*
              输出结果:
                  静态代码块
                  匿名代码块
                  构造方法
       */
              Persion persion1 = new Persion();
              
              System.out.println("==============");
              /*
              输出:
                  匿名代码块
                  构造方法
              分析:静态方法只输出一次;
               */
              Persion persion2 = new Persion();
          }
      }
      
  • 抽象类

    • abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类

    • 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明抽象类。

    • 抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。

    • 抽象方法:只有方法的声明,没有方法的实现,它是用来让子类实现的。

    • 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。

    • eg:

    • package OOP.Test17;
      
      public class Application {
          public static void main(String[] args) {
      //        Action action = new Action(); //报错,抽象类不能声明对象
          }
      }
      
    • package OOP.Test17;
      
      // abstract 抽象类
      public abstract class Action {
          // 约束~ 别人帮我们完成实现
          // abstract  抽象方法,只有方法的名字没有方法的实现!
          public abstract void doSomething();
      
          // 抽象类特点:
      //        1.不能new这个抽象类,只能靠子类去实现它:约束!
      //        2.抽象类中可以写普通方法~
      //        3.抽象方法必须在抽象类中
      //
      //    思考:
      //          1.抽象类中存在构造器吗?
      //          2.存在的意义
      }
      
    • package OOP.Test17;
      
      // 子类必须重写父类中的抽象方法,除非子类也是抽象类
      public class A extends Action {
          public void doSomething(){
              System.out.println("重写父类的抽象方法");
          }
      }
      
  • 接口(interfac)

    • 普通类:只有具体实现

    • 抽象类:具体实现和规范(抽象方法)都有!

    • 接口:只有规范!(自己无法写方法~专业的约束!约束和实现分离:面向接口编程)

    • 1

    • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。。。

    • 接口的本质是契约,就像我们人间的法律一样,制定好后大家都遵守。

    • 接口的精髓,是对对象的抽象,最能体现这一点的就是接口,为什么我们讨论设计 模式都只针对具备了抽象能力的语言(比如c++、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。

    • package OOP.Test18;
      
      // interface 定义的关键字,接口都需要有实现类
      public interface UserService {
           /*
           接口中定义常量
           // 常量~ publicstatic final
           // int AGE = 99;
            */
      
          // 接口抽象方法中不能有方法体
      //    public void run(){ //编译错误
      //    }
      
          // 接口中的所有定义其实都是抽象的 public abstract
          
           void add(String name);
           void delete(String name);
           void updata(String name);
           void query(String name);
      }
      
      package OOP.Test18;
      
      public interface TimeService {
          void timer();
      }
      
      package OOP.Test18;
      
      /*
      抽象类:继承用extends
      类 可以实现接口 implement 接口
      实现了接口的类,就需要重写接口中的方法~
       */
      // 利 用 接 口 实 现 了 多 个 继 承
      public class UserServiceImp1 implements UserService,TimeService {
          @Override
          public void timer() {
      
          }
      
          @Override
          public void add(String name) {
      
          }
      
          @Override
          public void delete(String name) {
      
          }
      
          @Override
          public void updata(String name) {
      
          }
      
          @Override
          public void query(String name) {
      
          }
      }
      
    • 接口作用

      作用:
          1.约束
          2.定义一些方法,让不同的人实现~ 10---->1
          3.public abstract
          4.public static final
          5.接口不能被实例化~,接口中没有构造方法~
          6.implements 可以实现多个接口
          7.必须要重写接口中的方法~
      
  • 内部类

    • 内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对于A类来说就成为内部类,而A类相对于B类来说就是外部类了。

    • 成员内部类

    • 静态内部类

    • 局部内部类

    • 匿名内部类

      • package OOP.Test19;
        
        public class Application {
            public static void main(String[] args) {
                Outer outer = new Outer();
                // 通过这个外部类来实例化内部类
                Outer.Inner inner = outer.new Inner();
                inner.in();// 输出结果为:这是内部类的方法
                inner.getId(); // 输出结果为:10
            }
        }
        
        package OOP.Test19;
        
        public class Outer {
        
            private int id = 10;
            public void out(){
                System.out.println("这是外部类的方法");
            }
            public class Inner{
                public void in(){
                    System.out.println("这是内部类的方法");
                }
        
                // 获得外部类的私有属性~
                public void getId(){
                    System.out.println(id);
                }
            }
            // 一个java类中可以有多个class类,但只能有一个public修饰的类
            class A{
            }
        // 局 部 内 部 类
            public void method(){
                class Inner{
        
                }
            }
        }
        
  • 异常机制

    • 实际工作中,遇到的情况不可能是非常完美的。比如:你写的某个模块,用户输入不一定符合你的要求、你的程序要打开某个文件,这个文件可能不存在或者文件格式不对,你要读取数据库的数据,数据可能是空的等。我们的程序在跑着,内存或硬盘可能满了。。。

    • 软件程序在运行过程中,非常可能遇到刚刚提到的这些异常问题,我们叫异常,英文是:Exception,意思是例外。这些,例外情况,或者叫异常,怎么让我们写的程序作出合理的处理。而不至于程序崩溃。

    • 异常指程序运行中出现的不期而至的各种情况,如:文件找不到、网络连接失败、非法参数等。

    • 异常发生在在程序运行期间,它影响了正常的执行流程。

    • 简单的分类:

      • 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在的文件时,一个异常就发生了,这些异常在编译时不能被简单的忽略。
      • 运行时异常:运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
      • 错误ERROR:错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的
    • 异常体系结构

      • java比异常当做对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。
      • 在java API中已经定义了许多异常类,这些异常类分为两大类,错误Error和异常Exception。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OfVbHOz0-1629187619149)(C:\Users\LXK\AppData\Roaming\Typora\typora-user-images\image-20210801104432535.png)]
    • 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通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常

      • package OOP.Test20;
        
        public class Application {
            public static void main(String[] args) {
                new Application().a();
            }
            public void a(){
                b();
            }
            public void b(){
                a();
            }
        }
        
  • 异常处理机制

    • 抛出异常

    • 捕获异常

    • 异常处理的五个关键字

      • try、catch、finally、throw、throws
    • package OOP.Test21.exception;
      
      public class Test {
          public static void main(String[] args) {
              int a = 1;
              int b = 0;
              new Test().test1(1,0);
              try{ //try监控区域
                  System.out.println(a/b);
              }catch (ArithmeticException e){ // catch 捕获异常
                  System.out.println("程序出现异常,变量b不能为0");
              }catch(Exception e){ // catch 捕获异常
                  System.out.println("Exception");
              } finally{ // 处理善后工作(可以不需要)
                  System.out.println("finally");
              }
      //        System.out.println(a/b); // ArithmeticException: / by zero
          }
      //    假设这个方法中,处理不了这个异常,则可在方法中抛出异常
          public void test1(int a,int b){
              if(b==0){ // throw  throws
                  throw new ArithmeticException();// 主动抛出异常,一般在方法中使用
              }
          }
      }
      
  • 自定义异常

    • 使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。

    • 在程序中使用自定义异常类,大体可以分为以下几个步骤:

      1. 创建自定义异常类。
      2. 在方法中通过throw关键字抛出异常对象。
      3. 如果当前抛出异常的方法中处理异常,可以使用try—catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
      4. 在出现异常方法的调用者中捕获并处理异常。
    • package OOP.Test22.Exception;
      
      // 自 定 义 的 异 常 类
      public class MyException extends Exception{
          // 传递数字>10
          public int detail;
      
          public MyException(int a) {
              this.detail = a;
          }
          // toString:异常的打印信息
          @Override
          public String toString() {
              return "MyException{" +
                      "detail=" + detail +
                      '}';
          }
      }
      
      package OOP.Test22.Exception;
      
      public class Test {
          // 可能会存在异常的方法
          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(1);
              }catch (MyException e){
                  System.out.println("MyException=>"+e);
              }
          }
      }
      
  • 实际应用中的经验总结

    • 处理运行时异常时,采用逻辑去合理规避同时辅助try—catch处理
    • 在多重catch块后面,可以加上一个catch(Exception)来处理可能会被遗漏的异常
    • 对于不确定的代码,也可以加上try-catch,处理潜在的异常
    • 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出
    • 具体如何处理异常,要根据不同的业务需求和异常类型去决定
    • 尽量添加finally语句块去释放占用的资源

urn “MyException{” +
“detail=” + detail +
‘}’;
}
}
```

```java
package OOP.Test22.Exception;

public class Test {
    // 可能会存在异常的方法
    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(1);
        }catch (MyException e){
            System.out.println("MyException=>"+e);
        }
    }
}
```
  • 实际应用中的经验总结

    • 处理运行时异常时,采用逻辑去合理规避同时辅助try—catch处理
    • 在多重catch块后面,可以加上一个catch(Exception)来处理可能会被遗漏的异常
    • 对于不确定的代码,也可以加上try-catch,处理潜在的异常
    • 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出
    • 具体如何处理异常,要根据不同的业务需求和异常类型去决定
    • 尽量添加finally语句块去释放占用的资源
posted @ 2022-07-17 19:08  小白到大牛  阅读(42)  评论(0)    收藏  举报