2.面向对象

一、类、对象:

  1. 什么是类?什么是对象?
    • 现实生活中是由很多很多对象组成的,基于对象抽出了类
    • 对象:软件中真实存在的单个个体/东西
      类:类型/类别,代表一类个体
    • 类是对象的模板/模子,对象是类的具体的实例
    • 类中可以包含:
      • 对象的属性/特征-----------------------------成员变量
      • 对象的行为/动作/功能----------------------方法
    • 一个类可以创建多个对象
  2. 如何创建类?如何创建对象?如何访问成员?
  package ooday01;
  //学生类
  public class Student {
      //成员变量----对象的属性
      String name;
      int age;
      String address;
  
      //方法-----对象的行为/功能
      void study(){
          System.out.println(name+"在学习...");
      }
      void sayHi(){
          System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
      }
  }
  
  package ooday01;
  //学生类的测试类
  public class StudentTest {
      public static void main(String[] args) {
          //创建一个学生对象
          Student zs = new Student();
          //给成员变量赋值
          zs.name = "zhangsan";
          zs.age = 18;
          zs.address = "河北廊坊";
          //调用方法
          zs.study();
          zs.sayHi();
  
          Student ls = new Student();
          ls.name = "lisi";
          ls.age = 25;
          ls.address = "黑龙江佳木斯";
          ls.study();
          ls.sayHi();
  
          //1)创建了一个学生对象
          //2)给所有成员变量赋默认值
          Student ww = new Student();
          ww.study();
          ww.sayHi();
  
      }
  }
  1. 方法的签名:方法名+参数列表

  2. 方法的重载(overload/overloading):-------------------------------方便用户的调用

    • 发生在同一类中,方法名相同,参数列表不同
      public class Aoo{
          void show(){}
          void show(String name){}
          void show(int age){}
          void show(String name,int age){}
          void show(int age,String name){}
      
          //int show(){ return 1; } //编译错误,重载与返回值类型无关
          //void show(String address){} //编译错误,重载与参数名称无关
      }
    
    • 编译器在编译时会根据方法的签名自动绑定方法
     public class OverloadDemo {
         public static void main(String[] args) {
             Aoo o = new Aoo();
             o.show();
             o.show("zhangsan");
             o.show(25);
             o.show("zhangsan",25);
             o.show(25,"zhangsan");
         }
     }
    

知识补充

  1. OO:面向对象
    OOA:面向对象分析
    OOD:面向对象设计
    OOP:面向对象编程--------------------------你们以后所参与的
  2. 高质量的代码:------------------------想拿年薪
    • 复用性好、扩展性好、维护性好、移植性好、健壮性好、可读性好、效率好…
  3. 类:是一种引用数据类型,是我们自己创造的一种数据类型
      引用
  数据类型  引用类型变量  指向      对象
  Student     zs        =   new Student();
  //读作:声明一个Student类型的引用zs,指向了一个学生对象
  1. 默认值:
 byte,short,int,long,char-----------------0
 float,double-----------------------------0.0
 boolean----------------------------------false
 引用类型----------------------------------null

二、构造方法、关键字this:

  1. 构造方法:构造函数、构造器、构建器-----------------------复用给成员变量赋初值的代码

    • 作用:给成员变量赋初值
    • 与类同名,没有返回值类型(连void都没有)
    • 在创建(new)对象时被自动调用
    • 若自己不写构造方法,编译器默认提供一个无参构造方法,若自己写了构造方法,则不再默认提供
    • 构造方法可以重载
     public class Student {
         String name; //成员变量(整个类中)
         int age;
         String address;
         //给成员变量赋初值
         Student(String name,int age,String address){ //局部变量(当前方法中)
             this.name = name;
             this.age = age;
             this.address = address;
         }
     
         void sayHi(){
             System.out.println("大家好,我叫"+name+",今年"+age+"岁了,家住"+address);
         }
     }
     
     public class ConsDemo {
         public static void main(String[] args) {
             //Student zs = new Student(); //编译错误,Student类没有无参构造方法
             Student zs = new Student("zhangsan",25,"LF");
             zs.sayHi();
     
             Student ls = new Student("lisi",24,"JMS");
             ls.sayHi();
         }
     }
    
  2. this:指代当前对象,哪个对象调用方法它指的就是哪个对象
    ​ 只能用在方法中,方法中访问成员变量之前默认有个this.
    this的用法:

    • this.成员变量名---------------------------访问成员变量

      当成员变量与局部变量同名时,若想访问成员变量,则this不能省略

    • this.方法名()--------------------------------调用方法(一般不用)
    • this()------------------------------------------调用构造方法(一般不用)
  3. null:表示空,没有指向任何对象。

    • 若引用的值为null,则该引用不能进行任何点操作了,若操作则发生NullPointerException空指针异常。

知识补充

  1. java规定:成员变量和局部变量是可以同名的,使用的时候默认采取的是就近原则
  2. 构造方法到底要不要参数,要看对象的数据能不能写死
    如果对象的数据都一样,意味着可以写死,就不需要传参。
    如果对象的数据都不一样,意味着不能写死,那就需要传参。
  3. 内存管理:由JVM来管理的
    • 堆:new出来的对象(包括成员变量)
    • 栈:局部变量(包括方法的参数)
    • 方法区:
    • null只是相对于引用类型而言,它与基本类型之间一点关系都没有
Student zs = null;
int a = null; //编译错误
class Aoo{
    int a; //成员变量
    void show(int b){ //局部变量
        int c = 5;
    }
}

new Aoo(); //--------堆中
a //-----------------堆中
b //-----------------栈中
c //----------------栈中
  1. 方向图
    在这里插入图片描述

  2. 堆栈内存图
    在这里插入图片描述

  3. null和NullPointerException
    在这里插入图片描述

三、引用数组类型、继承、关键字super:

  1. 引用类型数组:

    • 给元素赋值时,需要new个对象
    • 若想访问对象的数据,需要通过数组元素去打点来访问
     //对于引用类型数组而言,必须给每个元素都赋值的
     //若元素不赋值,则默认值为null,容易发生空指针异常
     Student[] stus = new Student[3]; //创建Student数组对象
     stus[0] = new Student("zhangsan",25,"LF"); //创建Student对象
     stus[1] = new Student("lisi",24,"JMS");
     stus[2] = new Student("wangwu",26,"SD");
     System.out.println(stus[0].name); //输出第1个学生的名字
     stus[1].age = 27; //修改第2个学生的年龄为27
     stus[2].sayHi(); //第3个学生跟大家问好
     for(int i=0;i<stus.length;i++){ //遍历所有学生
         System.out.println(stus[i].name); //输出每个学生的名字
         stus[i].sayHi(); //每个学生跟大家问好
     }
     
     Student[] stus = new Student[]{
         new Student("zhangsan",25,"LF"),
         new Student("lisi",24,"JMS"),
         new Student("wangwu",26,"SD")
     };
     
    
  2. 继承:

    • 作用:代码复用

    • 通过extends来实现继承

    • 超类/父类:共有的属性和行为
      派生类/子类:特有的属性和行为

    • 派生类可以访问派生类的+超类的,但超类不能访问派生类的

    • 一个超类可以有多个派生类,但一个派生类只能继承一个超类-----------单一继承

    • 继承具有传递性

    • java规定:构造派生类之前必须先构造超类

      • 在派生类的构造方法中若没有调用超类的构造方法,则默认super()调用超类的无参构造方法
       public class SuperDemo {
           public static void main(String[] args) {
               Boo o = new Boo();
           }
       }
       
       class Aoo{
           Aoo(){
               System.out.println("超类构造方法");
           }
       }
       class Boo extends Aoo{
           Boo(){
               super(); //默认的,调用超类的无参构造方法
               System.out.println("派生类构造方法");
           }
       }
      
      • 在派生类的构造方法中若自己调用了超类的构造方法,则不再默认提供

        注意:super()调用超类构造方法,必须位于派生类构造方法的第一行

       class Coo{
           Coo(int a){
           }
       }
       class Doo extends Coo{
           Doo(){
               super(5); //调用Coo的有参构造方法
           }
           /*
           //如下代码为默认的
           Doo(){
               super();
           }
            */
       }
      
  3. super:指代当前对象的超类对象

    super的用法:

    • super.成员变量名-------------------------访问超类的成员变量
    • super.方法名()-----------------------------调用超类的方法
    • super()---------------------------------------调用超类的构造方法

补充知识

  1. 基本数组类型内存图

在这里插入图片描述

  1. 引用型数组类型内存图

在这里插入图片描述

四、向上造型、重写与重载:

  1. 向上造型:--------------------------------------------代码复用

    • 超类型的引用指向派生类的对象
    • 能点出来什么,看引用的类型----------------这是规定,记住就OK

      何时向上造型:

      • 多种角色能干的事都一样的时候,可以将多种角色统一造型到超类数组中,实现代码复用

        eg: 学生/老师/医生都是输出名字+问好------干的事都一样,

        ​ 就可以将学生/老师/医生统一造型到Person数组中,这样一个for即可-------代码复用

     public class UploadDemo {
         public static void main(String[] args) {
             Aoo o1 = new Aoo();
             o1.a = 1;
             o1.show();
             //o1.b = 2;  //编译错误
             //o1.test(); //编译错误,超类不能访问派生类的
     
             Boo o2 = new Boo();
             o2.b = 1;
             o2.test();
             o2.a = 2;  //正确
             o2.show(); //正确,派生类可以访问超类的
     
             Aoo o3 = new Boo(); //向上造型
             o3.a = 1;
             o3.show();
             //o3.b = 2;  //编译错误
             //o3.test(); //编译错误,能点出来什么,看引用的类型
         }
     }
     
     class Aoo{
         int a;
         void show(){
         }
     }
     class Boo extends Aoo{
         int b;
         void test(){
         }
     }
    
  2. 方法的重写(override/overriding):重新写、覆盖

    • 发生在父子类中,方法名相同,参数列表相同
    • 重写方法被调用时,看对象的类型-----------------------这是规定,记住就OK
     class 餐馆{
         void 做餐(){ 做中餐 }
     }
     //1)我还是想做中餐-----------------不需要重写
     class Aoo extends 餐馆{
     }
     //2)我想改做西餐-------------------需要重写
     class Aoo extends 餐馆{
         void 做餐(){ 做西餐 }
     }
     //3)我想在中餐基础之上加入西餐--------需要重写(先super中餐,再加入西餐)
     class Aoo extends 餐馆{
         void 做餐(){
             super.做餐();
             做西餐
         }
     }
    
    • 重写需遵循"两同两小一大"原则:-------------------------了解,一般都是一模一样的
      • 两同:
        • 方法名相同
        • 参数列表相同
      • 两小:
        • 派生类方法的返回值类型小于或等于超类方法的

          • void和基本类型时,必须相等
          • 引用类型时,小于或等于
           //超类大,派生类小---------爸爸大,儿子小
           class Coo{
               void show(){}
               double test(){ return 0.0; }
               Student say(){ return null; }
               Person sayHi(){ return null; }
           }
           class Doo extends Coo{
               //int show(){ return 1; } //编译错误,void时必须相等
               //int test(){ return 0; } //编译错误,基本类型时必须相等
               //Person say(){ return null; } //编译错误,引用类型时必须小于或等于
               Student sayHi(){ return null; } //正确,Student小于Person
           }
          
        • 派生类方法抛出的异常小于或等于超类方法的

      • 一大:
        • 派生类方法的访问权限大于或等于超类方法的-
  3. 重写与重载的区别:----------常见面试题

    • 重写:发生在父子类中,方法名相同,参数列表相同
    • 重载:发生在同一类中,方法名相同,参数列表不同

补充知识

  1. 继承要符合is(是)的关系
  2. 超类的意义:
    • 封装共有的属性和行为----------------------实现代码复用
    • 为所有派生类提供了统一的类型----------向上造型(实现代码复用)

五、Package、访问控制修饰符、静态方法:

  1. package:声明包

    • 作用:避免类的命名冲突
    • 同包中的类不能同名,但不同包中的类可以同名
    • 类的全称:包名.类名,包名常常有层次结构
    • 建议:包名所有字母都小写
  2. import:导入类
    同包中的类可以直接访问
    不同包中的类不能直接访问,若想访问:
    先import导入类再使用类------------建议
    类的全称----------------------------------太繁琐,不建议

  3. 访问控制修饰符:------------------保护数据的安全

    • public:公开的,任何类

    • private:私有的,本类

    • protected:受保护的,本类、派生类、同包类

    • 默认的:什么也不写,本类、同包类

      说明:

      1. java不建议默认访问权限
      2. 类的访问权限只能是public或默认的,类中成员的访问权限如上4种都可以
      3. 访问权限由小到大依次为:private<默认的<protected<public
     class Card{ //银行卡
         private String cardId;  //卡号
         private String cardPwd; //密码
         private double balance; //余额
         
         public boolean payMoney(double money){ //支付金额---收银员可以调用
             if(balance>=money){
                 balance-=money;
                 return true;
             }else{
                 return false;
             }
         }
         public boolean checkPwd(String pwd){ //检测密码--营业员可以调用
             if(pwd和cardPwd相同){
                 return true;
             }else{
                 return false;
             }
         }
     }
    
    //访问控制符的演示
    public class Aoo {
        public int a;     //任何类
        protected int b;  //本类、派生类、同包类
        int c;            //本类、同包类
        private int d;    //本类
    
        void show(){
            a = 1;
            b = 2;
            c = 3;
            d = 4;
        }
    }
    
    class Boo{ //---------------------演示private
        void show(){
            Aoo o = new Aoo();
            o.a = 1;
            o.b = 2;
            o.c = 3;
            //o.d = 4; //编译错误
        }
    }
    
    package ooday05_vis;
    import ooday05.Aoo;
    public class Coo { //---------------演示同包
        void show(){
            Aoo o = new Aoo();
            o.a = 1;
            //o.b = 2; //编译错误
            //o.c = 3; //编译错误
            //o.d = 4; //编译错误
        }
    }
    
    class Doo extends Aoo{ //跨包继承---------演示protected
        void show(){
            a = 1;
            b = 2;
            //c = 3; //编译错误
            //d = 4; //编译错误
        }
    }
    
  4. final:最终的、不能改变的-----------------单独应用几率低

    • 修饰变量:变量不能被改变

      class Eoo{
          int a = 5;
          final int b = 5;
          void show(){
              a = 55;
              //b = 55; //编译错误,final修饰的变量,不能被改变
          }
      }
      
    • 修饰方法:方法不能被重写

      class Foo{
          void show(){}
          final void test(){}
      }
      class Goo extends Foo{
          void show(){}
          //void test(){} //编译错误,final修饰的方法,不能被重写
      }
      
    • 修饰类:类不能被继承

      final class Hoo{}
      //class Ioo extends Hoo{} //编译错误,final的类不能被继承
      class Joo{}
      final class Koo extends Joo{} //正确,不能当老爸,但能当儿子
      
  5. static:静态的

    • 静态变量:

      • 由static修饰
      • 属于类,存储在方法区中,一份
      • 常常通过类名点来访问
      • 何时用:所有对象所共享的数据(图片、音频、视频等)
       //演示静态变量
       class Loo{
           int a;
           static int b;
           Loo(){
               a++;
               b++;
           }
           void show(){
               System.out.println("a="+a+",b="+b);
           }
       }
       
       public class StaticDemo {
           public static void main(String[] args) {
               Loo o1 = new Loo();
               o1.show();
               Loo o2 = new Loo();
               o2.show();
               Loo o3 = new Loo();
               o3.show();
               System.out.println(Loo.b); //常常通过类名点来访问
           }
       }
      
    • 静态方法:

      • 由static修饰
      • 属于类,存储在方法区中,一份
      • 常常通过类名点来访问
      • 静态方法中没有隐式this传递,所以只能直接访问静态成员,而不能直接访问实例成员
      • 何时用:方法的操作与对象无关----方法中不需要访问对象的属性或行为
       //演示静态方法
       class Moo{
           int a; //实例变量(对象来访问)
           static int b; //静态变量(类名来访问)
           void show(){ //有隐式this
               System.out.println(this.a);
               System.out.println(Moo.b);
           }
           static void test(){ //没有隐式this
               //静态方法中没有隐式this传递
               //没有this就意味着没有对象
               //而实例成员a必须通过对象来访问
               //所以如下语句会发生编译错误
               //System.out.println(a); //编译错误
               System.out.println(Moo.b);
           }
       }
       //演示静态方法的应用场景
       class Noo{
           int a; //实例变量---描述对象的属性
           //show()方法中需要访问对象的属性a,说明show()的操作与对象有关---不能静态方法
           void show(){
               System.out.println(a);
           }
           //plus()方法中不需要访问对象的属性和行为,说明plus()的操作与对象无关-可以静态方法
           static void plus(int num1,int num2){
               int num=num1+num2;
               System.out.println(num);
           }
       }
       public class StaticDemo {
           public static void main(String[] args) {
               Noo.plus(5,7); //常常通过类名点来访问
           }
       }
      
    • 静态块:

      • 由static修饰
      • 属于类,在类被加载时自动执行,因为一个类只被加载一次,所以静态块也只执行一次
      • 何时用:加载/初始化静态资源(图片、音频、视频等)
       //演示静态块
       class Poo{
           static{
               System.out.println("静态块");
           }
           Poo(){
               System.out.println("构造方法");
           }
       }
       public class StaticDemo {
           public static void main(String[] args) {
               Poo o4 = new Poo();
               Poo o5 = new Poo();
           }
       }
      

补充知识

  1. 访问权限常规设置模式:

    • 数据(成员变量)私有化(private)
    • 行为(方法)公开化(public)------------------绝大部分方法都是公开的
  2. 成员变量分两种:

    • 实例变量:没有static修饰,属于对象的,存储在堆中,有几个对象就有几份
      ​ 通过对象/引用打点来访问
    • 静态变量:有static修饰,属于类的,存储在方法区中,只有一份
      ​ 常常通过类名点来访问
  3. 内存管理:由JVM来管理的

    • 堆:new出来的对象(包括实例变量、数组的元素)
    • 栈:局部变量(包括方法的参数)
    • 方法区:.class字节码文件(包括静态变量、所有方法)
  4. 图片:

    //  公开的  静态的   图片类型  变量名
        public static ImageIcon sea;
    
  5. 静态变量内存图

在这里插入图片描述

六、抽象方法、抽象类:

  1. static final常量:应用率高
    • 必须声明同时初始化
    • 由类名打点来访问,不能被改变
    • 建议:常量所有字母都大写,多个单词用_分隔
    • 编译器在编译时会将常量直接替换为具体的数,效率高
    • 何时用:数据永远不变,并且经常使用
  2. 抽象方法:
    • 由abstract修饰
    • 只有方法的定义,没有具体的实现(连{}都没有)
  3. 抽象类:
    • 由abstract修饰
    • 包含抽象方法的类必须是抽象类
    • 抽象类不能被实例化(new对象)
    • 抽象类是需要被继承的,派生类:
      • 重写抽象方法---------------变不完整为完整
      • 也声明为抽象类------------一般不这么用
    • 抽象类的意义:
      • 封装共有的属性和行为-------------------代码复用
      • 为所有派生类提供统一的类型----------向上造型(代码复用)
      • 可以包含抽象方法,为所有派生类提供统一的入口(向上造型后能点出来),同时可以达到强制必须重写的目的(相当于制定了一个标准)

补充知识

  1. 设计规则:
    • 将派生类所共有的属性和行为,抽到超类中-------------抽共性
    • 若派生类的行为(实现代码)都一样,设计为普通方法
      若派生类的行为(实现代码)都不一样,设计为抽象方法
  2. 抽象方法/抽象类的疑问:
    • 抽象方法存在的意义是什么?
      • 保证当发生向上造型时,通过超类的引用能点出来那个方法-------保证能点出方法来
    • 既然抽象方法的意义是保证能点出来,那为什么不设计为普通方法呢?
      • 设计为普通方法,意味着派生类可以重写也可以不重写,但设计为抽象方法,则可以强制派生类必须重写--------达到强制派生类重写,统一的目的

七、成员类部类、匿名类部类

  1. 成员类部类:

    • 类中套类,外面的称为外部类,里面的称为内部类
    • 内部类通常只服务于外部类,对外不具备可见性
    • 内部类对象通常在外部类中创建
    • 内部类中可以直接访问外部类的成员(包括私有的)
      • 内部类中有一个隐式的引用,指向了创建它的外部类对象
        -----------------------------------------外部类名.this
public class InnerClassDemo {
             public static void main(String[] args) {
               Mama m = new Mama();
               //Baby b = new Baby(); //编译错误,内部类对外不具备可见性
             }
           }
class Mama{ //外部类
  private String name;
  void create(){
    Baby b = new Baby();
  }
  class Baby{ //内部类
    void showName(){
        System.out.println(name); //简便写法
        System.out.println(Mama.this.name); //完整写法
        //System.out.println(this.name); //编译错误,this指代当前Baby对象
    }
  }
}
  1. 匿名内部类:----------大大简化代码的操作
    • 若想创建一个类(派生类)的对象,并且对象只被创建一次,此时可以设计为匿名内部类
    • 在匿名内部类中不能修饰外面局部变量的值,因为该变量在此处会默认为final的
    • 问:内部类有独立的.class吗?
      答:有
public class NstInnerClassDemo {
           public static void main(String[] args) {
             //1)系统自动创建了Aoo的一个派生类,但是没有名字
             //2)为该派生类创建了一个对象,名为o1
             //  ---new Aoo(){}是在创建Aoo的派生类的对象
             //3)大括号中的为派生类的类体
             Aoo o1 = new Aoo(){
             };
    //1)系统自动创建了Aoo的一个派生类,但是没有名字
    //2)为该派生类创建了一个对象,名为o2
    //3)大括号中的为派生类的类体
    Aoo o2 = new Aoo(){
    };
    //1)系统自动创建了Boo的一个派生类,但是没有名字
    //2)为该派生类创建了一个对象,名为o3
    //3)大括号中的为派生类的类体
    Boo o3 = new Boo(){
        void show(){
            System.out.println("showshow");
        }
    };
    o3.show();
    int num = 5;
    num = 55;
    Boo o4 = new Boo(){
        void show(){
            System.out.println("showshow");
            //num = 66; //编译错误,匿名内部类中不能修改外面局部变量的值,
                        //因为在此处该变量会默认为final的
        }
    };

   }
}

  abstract class Boo{
    abstract void show();
  }

  abstract class Aoo{
  }

补充知识

  1. 隐式的引用:
    • this:指代当前对象
    • super:指代当前对象的超类对象
    • 外部类名.this:指代当前对象的外部类对象
  2. 做功能的套路:
    • 先写行为/方法:
      • 若为派生类所特有的行为,则将方法设计在特定的类中
      • 若为所有派生类所共有的行为,则将方法设计在超类中
    • 窗口调用:
      • 若为定时(自动)发生的,则在定时器中调用
      • 若为事件触发的,则在侦听器中调用
  3. 调用方法的注意事项:
    • 1)若方法是有返回值的,则必须声明对应类型的变量来接收
    • 调用时:
      • 若与方法在同一个类中,则可以直接调用
      • 若与方法不在同一个类中,则需要通过引用名打点来调用
      • 若方法为静态的,则直接通过类名打点来调用
  4. 错误分类:
    • 编译错误----都是由于违反语法规则了
    • 异常--------运行时发生,找到at后的链接点击
    • 程序的运行结果与你所预期的结果不同------------必须得调错
  5. 如何调错:
    • 打桩: System.out.println(数据);

八、接口:

  1. 接口:

    • 是一种数据类型(引用类型)
    • 由interface定义
    • 只能包含常量和抽象方法(所有数据默认都是常量,所有方法默认都是抽象的)
    • 接口不能被实例化
    • 接口是需要被实现/继承的,实现/派生类:必须重写所有抽象方法
    • 一个类可以实现多个接口,用逗号分隔。若又继承又实现时,应先继承后实现。
    • 接口可以继承接口
     //接口的演示
     public class InterfaceDemo {
         public static void main(String[] args) {
             //Inter5 o = new Inter5(); //编译错误,接口不能被实例化
             Inter5 o1 = new Doo(); //向上造型
             Inter4 o2 = new Doo(); //向上造型
         }
     }
     
     //演示接口的语法
     interface Inter{
         public static final int NUM = 5;
         public abstract void show();
         int COUNT = 5; //默认public static final
         void test();   //默认public abstract
         //int number; //编译错误,常量必须声明同时初始化
         //void say(){ } //编译错误,抽象方法不能有方法体
     }
     
     //演示接口的实现
     interface Inter1{
         void show();
         void test();
     }
     class Aoo implements Inter1{
         public void show(){} //重写接口中的抽象方法时,必须加public权限
         public void test(){}
     }
     
     //演示接口的多实现
     interface Inter2{
         void show();
     }
     interface Inter3{
         void test();
     }
     abstract class Boo{
         abstract void say();
     }
     class Coo extends Boo implements Inter2,Inter3{
         public void show(){}
         public void test(){}
         void say(){}
     }
     
     //演示接口继承接口
     interface Inter4{
         void show();
     }
     interface Inter5 extends Inter4{
         void test();
     }
     class Doo implements Inter5{
         public void test(){}
         public void show(){}
     }
    

补充知识

  1. 类中成员的默认访问权限---------默认的
    接口中成员的默认访问权限------public的
    重写接口中的抽象方法时,必须加public权限
  2. 类和类------------------------继承
    接口和接口------------------继承
    类和接口---------------------实现
  3. 能够造型成为的类型:超类+所实现的接口
  4. 设计规则:
    • 将所有派生类所共有的属性和行为,抽到超类中-------------抽共性
    • 若派生类的行为(实现代码)都一样,设计为普通方法
      若派生类的行为(实现代码)都不一样,设计为抽象方法
    • 将部分派生类所共有的属性和行为,抽到接口中
      接口是对继承的单根性的扩展--------------实现多继承
      接口是一个标准、一种规范,实现了接口就能干某件事,不实现接口就干不了那个事
  5. 如何调错:---------------------帮助我们找到问题位置所在
    • 快速锁定问题方法:
      • 将方法调用的代码全都注释掉,然后一个一个放开,
        放开哪个出错了,说明问题就出在那个方法上。
    • 打桩: System.out.println(数据);

九、多态:

  1. 多态:多种形态

    • 同一个对象被造型为不同的类型时,有不同的功能-------所有对象都是多态的
      • 对象的多态:水、我、你…
        同一类型的引用在指向不同的对象时,有不同的实现----所有抽象方法都是多态的
      • 行为的多态:cut()、getImage()、move()…
    • 向上造型/自动类型转换:
      • 超类型的引用指向派生类的对象------前面是超类型,后面是派生类型
      • 能点出来什么,看引用的类型
      • 能造型成为的数据类型:超类+所实现的接口
    • 强制类型转换,成功的条件只有两种:
      • 引用所指向的对象,就是该类型
      • 引用所指向的对象,实现了该接口或继承了该类
    • 强转时若不满足如上条件,则发生ClassCastException类型转换异常
      建议:在强转之前先通过instanceof来判断引用指向的对象是否是该类型

      说明:instanceof会返回true或false的结果

      ​ 如果满足强转成功的条件则返回true,否则返回false

      何时需要强转:向上造型后,若想访问的东西在超类中没有,则需要强转

     public class MultiTypeDemo {
         public static void main(String[] args) {
             //成功的条件1:引用所指向的对象,就是该类型
             //成功的条件2:引用所指向的对象,实现了该接口或继承了该类
             Aoo o = new Boo(); //向上造型
             Boo o1 = (Boo)o; //引用o所指向的对象,就是Boo类型-----------符合条件1
             Inter o2 = (Inter)o; //引用o所指向的对象,实现了Inter接口---符合条件2
             //Coo o3 = (Coo)o; //运行时会发生ClassCastException类型转换异常
             if(o instanceof Coo){ //false
                 Coo o4 = (Coo)o;
             }else{
                 System.out.println("o不是Coo类型");
             }
     
         }
     }
     interface Inter{ }
     class Aoo{ }
     class Boo extends Aoo implements Inter{ }
     class Coo extends Aoo{ }
    

补充知识

  1. 体会接口的好处:
 //复用性好、扩展性好、维护性好------------------高质量代码
 //被撞的是ObserveSubmarine-----调用ObserveSubmarine的getScore()-----10分
 //被撞的是TorpedoSubmarine-----调用TorpedoSubmarine的getScore()-----40分
 //被撞的是NuclearSubmarine-----调用NuclearSubmarine的getScore()-----100分
 if(s instanceof EnemyScore){ //------适用于所有实现EnemyScore接口的
     EnemyScore es = (EnemyScore)s;
     score += es.getScore();
 }
 //被撞的是MineSubmarine--------调用MineSubmarine的getLife()---------1
 //被撞的是NuclearSubmarine-----调用NuclearSubmarine的getLife()------3
 if(s instanceof EnemyLife){ //-------适用于所有实现EnemyLife接口的
     EnemyLife el = (EnemyLife)s;
     int num = el.getLife();
     ship.addLife(num);
 }
                                       
 //复用性差、扩展性差、维护性差------------------垃圾代码
 if(s instanceof ObserveSubmarine){ //---------只能适用于ObserveSubmarine的
     ObserveSubmarine os = (ObserveSubmarine)s;
     score += os.getScore();
 }
 if(s instanceof TorpedoSubmarine){ //---------只能适用于TorpedoSubmarine的
     TorpedoSubmarine ts = (TorpedoSubmarine)s;
     score += ts.getScore();
 }      
 if(s instanceof MineSubmarine){    //---------只能适用于MineSubmarine的
     MineSubmarine ms = (MineSubmarine)s;
     int num = ms.getLife();
     ship.addLife(num);
 }
 if(s instanceof NuclearSubmarine){ //---------只能适用于NuclearSubmarine
     NuclearSubmarine ns = (NuclearSubmarine)s;
     score += ns.getScore();
     int num = ns.getLife();
     ship.addLife(num);
 }

十、内存管理、String、字符池常量:

  1. 内存管理:由JVM管理

    • 堆:
      • 存储new出来的对象(包括实例变量、数组的元素)
      • 垃圾:没有任何引用所指向的对象
        垃圾回收器(GC)不定时到内存堆中清扫垃圾,回收过程是透明的(看不到的),
        不一定一发现垃圾就立刻回收,通过调用System.gc()可以建议虚拟机尽快调度GC来回收
      • 实例变量的生命周期:
        ​ 创建(new)对象时存储在堆中,对象被回收时一并被回收
      • 内存泄漏:不再使用的对象还没有被及时的回收,严重的泄漏会导致系统的崩溃
        建议:不再使用的对象应及时将引用设置为null
    • 栈:
      • 存储正在调用的方法中的局部变量(包括方法的参数)
      • 调用方法时,会在栈中为该方法分配一块对应的栈帧,栈帧中存储局部变量(包括方法的参数),
        方法调用结束时,栈帧被自动清除,局部变量一并被清除。
      • 局部变量的生命周期:
        ​ 调用方法时存储在栈中,方法结束时与栈帧一并被清除
    • 方法区:
      • 存储.class字节码文件(包括静态变量、所有方法)
      • 方法只有一份,通过this来区分具体的调用对象
  2. 面向对象三大特征总结:

    • 封装:

      • 类:封装的是对象的属性和行为
      • 方法:封装的是具体的业务逻辑功能实现
      • 访问控制修饰符:封装的是具体的访问权限
    • 继承:

      • 作用:代码复用
      • 超类:所有派生类所共有的属性和行为
        接口:部分派生类所共有的属性和行为
        派生类:派生类所特有的属性和行为
      • 单一继承、多接口实现,具有传递性
    • 多态:

      • 所有对象都是多态的,通过向上造型来体现
        所有抽象方法都是多态的,通过方法的重写来体现
      • 向上造型、强制类型转换、instanceof判断
  3. String字符串类型:

    • java.lang.String类使用final修饰,不能被继承
    • String的底层封装的是一个字符数组
    • String在内存中采用Unicode编码格式,每个字符占用2个字节的空间
    • 字符串对象一旦创建,对象内容永远无法改变,但字符串引用可以重新赋值(指向新的对象)
      • 不变对象
  4. 字符串常量池:

    • java对String字符串有一个优化措施:字符串常量池(堆中)
    • java推荐我们使用字面量/直接量(直接"")的方式来创建对象,并且会缓存所有以字面量形式创建的字符串对象到常量池中,当使用相同字面量再创建对象时将会复用常量池中的对象,以减少内存开销
     /*
         使用字面量来创建字符串对象时,JVM会检查常量池中是否有该对象:
         1)若没有,则会创建该字符串对象,并存入常量池中
         2)若有,则直接将常量池中的对象(引用)返回---并不会创建新的字符串对象
     */
     String s1 = "123abc"; //常量池还没有,因此创建该字符串对象,并存入常量池中
     String s2 = "123abc"; //常量池中已经有了,直接复用对象
     String s3 = "123abc"; //常量池中已经有了,直接复用对象
     //引用类型==,比较的是地址是否相同-----这是规定
     System.out.println(s1==s2); //true
     System.out.println(s1==s3); //true
     System.out.println(s2==s3); //true
     
     s1 = s1 + "!"; //创建新的字符串对象(123abc!)并将地址赋值给s1
     System.out.println(s1==s2); //false
    

补充知识

在这里插入图片描述

在这里插入图片描述

  1. 字符串常量池
    在这里插入图片描述

  2. 方法区

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值