JAVA面向对象

1.面向对象编程(基础)
1.1类与对象
  1. 类就是数据类型

  2. 对象就是一个具体的实例

    //创建一个猫对象(实例化)
    Cat cat1 = new Cat();
    cat1.name = "**";
    cat1.age = 2;
    cat1.color = "橘色"
    
    //创建猫类
    class Cat{
        //属性
        //名字
        String name;
        //年龄
        int age;
        //颜色
        String color;
        public void speak(){
            System.out.println("喵喵喵");
        }
    }
    
  3. 对象在内存中存在形式

    在这里插入图片描述

    ​ 【注意

    ​ — 基本数据类型放在堆中,引用类型和字符串放在方法区中,方法区还存放类信息(属性信息和方法信息)

  4. 属性/成员变量

    • 从概念或叫法上看: 成员变量 = 属性 = field(字段) 即成员变量是用来表示属性的
    • 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)

    注意事项

    • 属性的定义语法同变量 访问修饰符 属性类型 属性名 四种访问修饰符: public protected 默认 private
    • 属性的定义类型可以为任意类型,包含基本类型或引用类型
    • 属性如果不赋值,有默认值,规则和数组一样
  5. 创建对象

    • 先声明再创建

      Cat cat;
      cat = new Cat();
      
    • 直接创建

      Cat cat = new Cat();
      
  6. 访问属性

    • 基本语法 对象名.属性名
  7. 类和对象的内存分配机制(重要)

    Person p1 = new Person();
    p1.age = 10;
    p1.name = "张三";
    Person p2 = p1;
    System.out.println(p2.age);
    

在这里插入图片描述

8.类和对象的内存分配机制

  • 栈:一般存放基本数据类型(局部变量)

  • 堆:存放对象

  • 方法区:常量池,类加载信息

  • 简单分析

    Person p = new Person();
    p.name = "jack";
    p.age = 12;
    

    【创建对象的流程简单分析】

    • 先加载Person类信息(属性和方法信息,只会加载一次)
    • 在堆中分配空间,进行默认初始化
    • 把地址赋给p,p就指向对象
    • 进行指定初始化
1.2成员方法
  1. 基本介绍

    • 在某些情况下,需要定义成员方法,对类进行完善
  2. 成员方法快速入门

    public static void main(String[] args) {
            Scanner scan = new Scanner(System.in);
            Person p = new Person();
            p.speak();
            p.sumNum();
            System.out.println("请输入1~n的数字,计算和");
            p.sumNum2(scan.nextInt());
            System.out.println("请输入需要求和的两个数字");
            int sum = p.getSum(scan.nextInt(), scan.nextInt());
            System.out.println(sum);
        }
    
    class Person{
        String name;
        int age;
        String sex;
        //成员方法  public表示方法公开   void表示没有返回值   speak表示方法名  ()可以添加形参列表  {}方法体
        public void speak(){
            System.out.println("我是一个好人");
        }
        public void sumNum(){
            int sum = 0;
            for (int i = 1;i <= 100; i++){
                sum += i;
            }
            System.out.println(sum);
        }
        public void sumNum2(int num){
            int sum = 0;
            for (int i = 1;i <= num; i++){
                sum += i;
            }
            System.out.println(sum);
        }
        public int getSum(int num1,int num2){
            int sum = num1 + num2;
            return sum;
        }
    }
    
  3. 方法的调用机制【图示
    在这里插入图片描述

    ​ 【总结】

    ​ — 当程序执行到方法时,会开辟一个独立的空间(栈空间)

    ​ — 当方法执行完毕,或者执行到return语句时,就会返回,并且销毁方法

    ​ — 返回到调用方法的地方

    ​ — 返回后,继续执行方法后面的代码

    ​ — 当main方法(栈)执行完毕,整个程序退出

  4. 成员方法的定义

    修饰符 返回数据类型 方法名 (形参列表){
        //方法体
        语句;
        return 返回值;
    }
    
    • 形参列表:表示成员方法输入参数
    • 数据类型(返回类型):表示成员方法输出,void表示没有返回值
    • 方法主体:表示为了实现某一个功能
    • return 语句不是必须的
    • 如果方法没有返回值void,可以不写,也可以return;
  5. 方法递归(recursion)的调用

    public class TwoDimensionalArray02 {
        public static void main(String[] args) {
            Recursion recursion = new Recursion();
            recursion.test(4);
        }
    
    }
    class Recursion{
        public void test(int n){
            if(n > 2){
                test(n - 1);
            }
            System.out.println("n="+n);
        }
    }
    //输出结果: n = 2   n = 3 n = 4
    

    【图示流程】
    在这里插入图片描述

    ​ 【注意】 方法栈执行结束,会被cg回收

  6. 递归重要规则

    • 执行一个方法时,就创建一个新的受保护的独立空间(栈)

    • 方法的局部变量是独立的,不会相互影响

    • 如果方法中使用的是引用类型变量(比如数组、对象),就会共享该引用类型的数据

    • 递归必须退出递归的条件逼近,否者就会无限递归,出现栈溢出

    • 当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕

      【【递归案例—老鼠寻路】】

      public class TwoDimensionalArray02 {
          public static void main(String[] args) {
              Maze maze = new Maze();
              maze.getMap();
          }
      
      }
      class Maze{
          //生成地图
          public void getMap(){
              int[][] map = new int[8][7];
              for (int i = 0;i < map[1].length;i++){
                  map[0][i] = 1;
                  map[map.length-1][i] = 1;
              }
              for (int j = 0; j < map.length; j++){
                  map[j][0] = 1;
                  map[j][map[1].length-1] = 1;
              }
              map[3][1] = 1;
              map[3][2] = 1;
              for (int k = 0; k < map.length; k++){
                  for(int l = 0;l < map[k].length;l++){
                      System.out.print(map[k][l]+"\t");
                  }
                  System.out.println();
              }
              track(map,1,1);
              //老鼠运动的轨迹
              for (int k = 0; k < map.length; k++){
                  for(int l = 0;l < map[k].length;l++){
                      System.out.print(map[k][l]+"\t");
                  }
                  System.out.println();
              }
          }
      
          //图标位移
          /**
           *
           * @param map
           * @param i
           * @param j
           * @return 布尔值
           *
           * 1.如果找到就返回true,否则返回false
           * 2.map是一个二维数组,即表示迷宫
           * 3.i,j就是老鼠的位置,初始化的位置为(1,1)
           * 4.使用递归找路线,先规定map数组的各个值的含义   0表示可以走(还没走) 1表示障碍物  2表示可以走(走了)    3表示走过,但是走不通是死路
           * 5.map[6][5] = 2 就说明找到通路,就可以结束,否则就继续找
           * 7.老鼠找路策略  下-》右-》上-》左
           */
          public boolean track(int[][] map,int i,int j){
              if (map[6][5] == 2){
                  return true;
              }else{
                  if (map[i][j] == 0){
                      //假定可以走通
                      map[i][j] = 2;
                      //使用找路策略,来确定该位置是否真的可以走通
                      if(track(map,i + 1,j)){
                          return true;
                      }else if(track(map,i,j + 1)){
                          return true;
                      }else if(track(map,i - 1,j )){
                          return true;
                      }else if (track(map,i,j - 1)){
                          return true;
                      }else{
                          map[i][j] = 3;
                          return false;
                      }
                  }else{
                      return false;
                  }
              }
          }
      }
      

      【【递归案例—汉罗塔】】

      public class HanoiTower {
          public static void main(String[] args) {
              Tower tower = new Tower();
              tower.move(5,'A','B','C');
          }
      }
      class Tower{
          //num  表示要移动的个数   a,b,c分别表示A塔 B塔 c塔
          public void move(int num,char a,char b,char c){
              if(num == 1){
                  System.out.println(a+"--->"+c);
              }else{
                  //先移动上面所有的盘到b盘,借组c
                  move(num - 1,a,c,b);
                  System.out.println(a+"--->"+c);
                  //再把b塔的所有盘,移动到c,借助a
                  move(num - 1,b,a,c);
              }
          }
      }
      

      【【递归案例—八皇后】】

    public class EightQueens {
        /**
         * 数组最大容量
         *
         */
        private int max = 8;
        //记录列的数组
        private int[] arr = new int[max];
        //解法数
        private static int wayCount = 0;
        //回溯次数
        private static int recursionCount = 0;
    
    
        /**
         * 检测通路
         * @param n
         */
        public void checkWay(int n){
            if (n == max){
                showWay();
                return;
            }
            //给列赋值,同时判断通路
            for(int i = 0;i < max;i++){
                //给列赋值
                arr[n] = i;
                //如果是通路
                if(judgeWay(n)){
                    checkWay(n+1);
                }
            }
        }
    
        /**
         * 判断是否为通路
         *
         * @param n
         * @return
         */
        public boolean judgeWay(int n){
            recursionCount++;
            for (int i = 0;i < n; i++){
                if (arr[i] == arr[n] || Math.abs(n - i) == Math.abs(arr[n] - arr[i])){
                    return false;
                }
            }
            return true;
        }
        public void showWay(){
            wayCount++;
            for (int i = 0;i < arr.length;i++){
                System.out.print(arr[i]+"\t");
            }
            //换行
            System.out.println();
        }
        
        public static void main(String[] args) {
            EightQueens eightQueens = new EightQueens();
            eightQueens.checkWay(0);
            System.out.println("wayCount = "+wayCount);
            System.out.println("recursionCount = "+recursionCount);
        }
    }
    
    
1.3成员方法传参机制
  1. 基本数据类型的传参机制
public class TwoDimensionalArray02 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        Param p = new Param();
        p.swap(a,b);
        System.out.println("a="+a+";b="+b);// a=10  b=20
    }
}
//开辟一个独立的空间(栈空间)
class Param{
    public void swap(int a,int b){
        int tmp = a;
        a = b;
        b = tmp;
        System.out.println("a和b交换后的值"+";\ta="+a+"\tb="+b);//a=20 b=10
    }
}
  1. 引用数据类型的传参机制(传递的地址,可以通过形参影响实参)

    public class TwoDimensionalArray02 {
        public static void main(String[] args) {
            Param param = new Param();
            int[] arr = {10,20,30};
            param.updateArr(arr);
            for (int num : arr){
                System.out.print(num+"\t");//100 20 30
            }
        }
    }
    /**
    *1.创建Parma对象,指向堆;创建数组,指向堆
    *2.产生一个新栈方法空间,引用类型传递是地址,指向堆
    *3.在堆中找到arr第一个元素修改为100,方法中的for循环,输出  100 20 30,方法结束
    *4.回到主栈,通过for循环遍历,arr指向还是堆中的位置,输出 100 20 30 
    *【注意】
    *	在方法中,如果将引用类型的地址置空,并不会影响主栈(main)的引用
    *	public void setNUm(person person){
    *		person = null;
    *	} 
    *
    *
    */
    class Param{
       public void updateArr(int[] arr){
           arr[0] = 100;
           for(int num : arr){
               System.out.print(num+"\t");// 100 20 30
           }
           System.out.println();
       }
    }
    
1.4overload
  1. 基本介绍

    • java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致

      public class overload_01 {
          public static void main(String[] args) {
      
              MyCalulator myCalulator = new MyCalulator();
              System.out.println(myCalulator.calculator(1,2));// 4
              System.out.println(myCalulator.calculator(2.1,2));// 4.1
              System.out.println(myCalulator.calculator(2,2.5));// 4.5
              System.out.println(myCalulator.calculator(1,2,3));// 6
          }
      }
      class MyCalulator{
          public int calculator(int num,int numx){
              return num+numx;
          }
          public double calculator(int numx,double num){
              return num+numx;
          }
          public double calculator(double num,int numx){
              return num+numx;
          }
          public int calculator(int num,int numx,int nums){
              return num+numx+nums;
          }
      }
      
  2. 注意事项和使用细节

    • 方法名相同
    • 形参列表不同,形参类型个数顺序,至少有一个不同,参数名无要求
    • 返回类型无要求
1.5可变参数
  1. 基本概念

    • java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。就可以通过可变参数实现
  2. 基本语法

    • 访问修饰符 返回类型 方法名(数据类型… 形参名){ }

      public class VarParamter {
          public static void main(String[] args) {
              VarParameter paramter = new VarParameter();
              System.out.println(paramter.sum(1,5,8,19));// 33
          }
      }
      class VarParameter{
          //int... 表示接受的是可变参数,类型是int,即可以接收多个int(0 ~多)
          public int sum(int... nums){
              int res = 0;
              for (int i = 0; i < nums.length; i++){
                  res += nums[i];
              }
              return res;
          }
      }
      
  3. 可变参数的注意事项

    • 可变参数的实参可以为0个任意多个

    • 可变参数的实参可以为数组

      //方法可以直接传递数组
      .方法名(数组)
      修饰符 返回类型 方法名 (数据类型... 形参名){}
      
    • 可变参数的本质就是数组

    • 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数的最后*

    • 一个形参列表中只能出现一个可变参数

1.6作用域
  1. 基本使用

    • 面向对象中,变量作用域是非常重要知识点,相对来说不是特别好理解
    • 在Java编程中,主要的变量就是属性(成员变量)和局部变量
    • 局部变量一般是指在成员方法中定义的变量
    • java作用域的分类
      • 全局变量,也就是属性,作用域(scope)为整个类体
      • 局部变量,也就是出了属性之外的其他变量,作用域为定义它的代码块中
    • 全局变量可以不赋值,直接使用,因为有默认值局部变量必须赋值后,才能使用,局部变量没有默认值
  2. 注意事项和使用细节

    • 属性局部变量可以重名,访问时遵循就近原则

    • 在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名

    • 属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡;局部变量,声明周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡,即在一次方法调用过程中

    • 修饰符不同

      • 全局变量/属性可以加修饰符
      • 局部变量不可以加修饰符
    • 作用域范围不同

      • 全局变量:可以被本类使用,或其他类使用(通过对象调用)
      • 局部变量:只能在本类中对应的方法中使用
      //属性和局部变量,访问时遵循就近原则
      public class VarScope {
          public static void main(String[] arg){
              VarParameters varParameters = new VarParameters();
              System.out.println(varParameters.sum(23));// 46
          }
      }
      class VarParameters{
          int num = 12;
          public int sum(int numx){
              int num = 23;
              return num + numx;
          }
      }
      
1.7构造器
  1. 基本语法

    • [修饰符] 方法名 (形参列表){ 方法体; }
    • 说明
      • 构造器的修饰符可以默认
      • 构造器没有返回值
      • 方法名名字必须一样
      • 形参列表和成员方法一样的规则
      • 构造器的调用系统来完成
  2. 基本介绍

    • 构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成了新对象的初始化,以下是它的特点:
      • 方法名和类名相同
      • 没有返回值
      • 创建对象时,系统会自动调用该类的构造器完成对象的初始化
    //简单的构造器演示
    public class Constructors {
        public static void main(String[] args) {
            Student student = new Student("张三",20);
            System.out.println(student.name+";"+student.age);
        }
    }
    class Student{
        String name;
        int age;
        public Student(){
        }
        public Student(String name,int age){
            this.name = name;
            this.age = age;
        }
        public Student(String name){
            this.name = name;
        }
        public Student(int age){
            this.age = age;
        }
    }
    
  3. 注意事项和使用细节

    • 一个类可以定义多个不同构造器,即构造器重载

    • 构造器名和类名要相同

    • 构造器没有返回值

    • 构造器时完成对象的初始化,并不是创建对象

    • 在创建对象时,系统自动的调用该类的构造器

    • 如果没有定义构造器系统默认给类生成一个默认无参构造方法(可以通过javap指令 反编译)

      • 使用语法
        • javap -c -v 类名
    • 一旦定义了自己的构造器,默认的构造器就被覆盖,就不能再使用无参构造器,可以再次定义无参构造器

    • 构造器可以被private修饰,类的构造器私有化,因此类不能继承,同时也不能被实例化

      public class Constructors {
          public static void main(String[] args) {
              System.out.println(Student.getInstance().name);
          }
      }
      class Student{
          String name;
          int age;
          
          private static final Student INSTANCE = new Student("李氏",25);
          public static Student getInstance(){
              return INSTANCE;
          }
          private Student(String name,int age){
              this.name = name;
              this.age = age;
          }
      }
      
1.8this
1.对象创建的流程分析
public class ThisKey {
    public static void main(String[] args) {
        /**
         * 1.在方法区中加载Persons类
         * 2.在堆中开辟一个对象空间(假设地址  0x11111),属性最初进行默认值初始化
         * 3.再查看类是否赋值,如果有值,就替换默认值
         * 4.构造器初始化,常量池存放String(非基本类型),堆中name属性指向常量池(假设地址 0x111122)
         * 5.栈中创建persons指向堆中的对象(假设地址  0x11111)
         */
        Persons persons = new Persons("张三",23);
        System.out.println(persons.name+";"+persons.age);
    }
}
class Persons{
    String name;
    int age;
    Persons(){}
    Persons(String name,int age){}
}
2.this概念
  1. java虚拟机会给每个对象分配一个this,代表当前对象
  2. 哪个对象调用,this就代表哪个对象
3.注意事项和使用细节
  • this关键字可以用来访问本类的属性、方法、构造器等
  • this用于区分当前类的属性和局部变量
  • 访问成员方法的语法: this.方法名(形参列表)
  • 访问构造器语法: this(形参列表) 【注意】只能在构造器中使用
  • this不能在类定义的外部使用,只能在类定义的方法中使用
2.面向对象编程(中级)
2.1包
1.包的三大作用
  • 区分相同名字的类
  • 当类很多时,可以很好的管理类
  • 控制访问范围
2.包基本语法
package com.huawei.**;
//说明
1.package 关键字,表示打包
2.com.huawei.**  表示包名
3.包的本质分析(原理)
  • 包的本质: 实际上就是创建不同的文件夹来保存类文件
4.包的命名
  • 命名规则
    • 只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
  • 命名规范
    • 一般是小写字母+小圆点
    • com.公司名.项目名.业务模块名
5.引入包
  • 【建议】 需要使用哪个类,就引入哪个类
//表示只会引入java.util包下的Scanner
import java.util.Scanner;
//表示引入java.util包下的所有类
import java.util.*;
2.2访问修饰符
1.基本介绍
  • java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围)
    • 公开级别: public修饰,对外公开
    • 受保护级别: protected修饰,对子类和同一个包中的类公开
    • 默认级别:没有修饰符符号,向同一个包的类公开
    • 私有级别:private修饰,只有类本身可以访问,不对外公开

【使用的注意事项

  • 修饰符可以用来修饰类中的属性、成员方法以及类
  • 只有默认的和public才能修饰类,并且遵循上述访问权限的特点
  • 成员方法的访问规则和属性完全一样
2.3封装(encapsulation)
1.封装介绍
  • 封装就是把抽象出来的数据[属性]和对数据操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(方法),才能对数据进行操作
2.封装的理解和好处
  • 隐藏实现细节
  • 可以对数据进行验证,保证安全合理
3.封装实现步骤
  1. 将属性进行私有化(private)

  2. 提供一个公共的set方法,用于对属性判断并赋值

    public void setXxx(类型 参数名){
        //加入数据验证的业务逻辑
        this.属性 = 参数名;
    }
    
  3. 提供一个公共的get方法,用于获取属性的值

    public XX getXxxx(){
        return XX;
    }
    
2.4继承
1.基本介绍
  • 继承可以解决代码复用,让我们的编程更加靠近人类的思维,当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可

  • 示意图

在这里插入图片描述

2.继承的基本语法
class 子类 extends 父类{}

//1.子类就会自动拥有父类定义的属性和方法
//2.父类又叫超类或基类
//3.子类又叫派生类
3.继承的优势
  1. 代码的复用性提高了
  2. 代码的扩张性和维护性提高了
4.继承使用细节
  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供的公共方法去访问

    class Person{
        public String username;
        protected int age;
        String jod;
        private double salary;
        //基类可以提供公共方法来实现
        public double getSal(){
            return this.salary;
        }
        private void salaryDetail(){
            System.out.println("多个项目构成")
        }
        public void getSalaryDetail(){
            salaryDetail();
        }  
    }
    class employees extends Person{
       //在子类中访问salary 
        getSal();
        //在子类中访问salaryDetail()fangfa
        getSalaryDetail();
        
    }
    
  2. 子类必须调用父类的构造器,完成父类的初始化

  3. 创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过

    class Person{
        public String username;
        protected int age;
        String jod;
        private double salary;
        public Person(String username,int age){
            this.username = username;
            this.age = age;
            System.out.println("父类Person(String username,int age)构造器被调用");
        }
    }
    class Stu extends Person{
        public Stu(){
            super("Tom",20);
            System.out.println("子类Stu()构造器被调用")
        }
        public Stu(String username){
            //如果在此处不指定,则编译不会通过
            super("Tom",20);
            System.out.println("子类Stu(String username)构造器被调用")
        }
    }
    class Test{
        public static void main(String[] args){
            Stu stu = new Stu("张三");
        }
    }
    
    //父类Person(String username,int age)构造器被调用
    //子类Stu(String username)构造器被调用
    
  4. 如果希望指定去调用父类的某个构造器,则显式调用一下

    super([参数列表]);
    
  5. super在使用时,需要放在构造器第一行(super只能在构造器中使用)

  6. super()和this()都只能放在构造器第一行,因此这两个方法不能共同存在一个构造器中(this()调本类的构造器)

  7. java所有类都是Object类的子类,Object是所有类的基类

  8. 父类构造器的调用不限于直接父类,将一直往上追溯到Object类(顶级类)

  9. 子类最多只能继承一个父类(指直接继承),即java中是单继承机制

  10. 不能滥用继承,子类父类之间必须满足is-a的逻辑关系

5.继承的本质【重点】
  • 当子类对象创建后,建立查找关系

  • 查找关系

    • 首先看看子类是否有该属性,如果子类有这个属性,并且可以访问,则返回信息
    • 如果子类没有这个属性,就查看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息)
    • 如果父类没有就按照(3)的规则,继续找上级父类,知道Object…

在这里插入图片描述

2.5多态
1.多态的基本介绍
  • 方法或者对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础上的

  • 对象的多态

    • 一个对象的编译类型和运行类型可以不一致
    • 编译类型在定义对象时,就确定了,不能改变
    • 运行类型时可以变化的
    • 编译类型是看定义时等号的左边运行类型看等号的右边
    public class Animal {
        public void cry(){
            System.out.println("动物在叫");
        }
    }
    --------------------------------------------------------------------------------
    public class Dog extends Animal {
        public void cry(){
            System.out.println("汪汪汪");
        }
    }
    --------------------------------------------------------------------------------
    public class Cat extends Animal {
        public void cry(){
            System.out.println("喵喵喵");
        }
    }
    -------------------------------------------------------------------------------
    public class PolyObject {
        public static void main(String[] args) {
            //ann 编译类型是Animal,运行类型Animal
            Animal ann = new Animal();
            ann.cry();//动物在叫
            // ann 编译类型是Animal,运行类型是Cat
            ann = new Cat();
            ann.cry();//喵喵喵
            // ann 编译类型是Animal,运行类型是Cat
            ann = new Dog();
            ann.cry();//汪汪汪
        }
    }
    
    
2.多态注意事项和细节讨论
  • 多态的前提是:两个对象(类)存在继承关系
  • 多态的向上转型
    • 本质: 父类的引用指向了子类的对象
    • 语法: 父类类型 引用名 = new 子类类型();
    • 特点: 编译类型看左边,运行类型看右边
      • 可以调用父类中的所有成员(需要遵守访问权限)
      • 不能调用子类中特有成员
      • 最终运行效果看子类的具体实现,即调用方法时,按照从子类开始查找方法,然后调用。
      • 编译阶段,能调用哪些成员,由编译类型决定的
  • 多态的向下转型
    • 语法: 子类类型 引用名 = (子类类型) 父类类型;
    • 只能强转父类的引用,不能强转父类的对象
    • 要求父类的引用必须指向的是当前目标类型的对象
    • 当向下转型后,可以调用子类类型所有成员
//向上转型
Animal animal = new Cat();
//向下转型
Cat cat = (Cat) animal;

//会报ClassCastException,类异常   animal指向堆中的cat对象,不能转为dog对象
Dog dog = (Dog) animal;
  • 属性没有重写之说!属性的值看编译类型【重点】

    public class PolyDetail2 {
        public static void main(String[] args) {
            Base base = new Sub();
            System.out.println(base.count);//10
            Sub sub = (Sub) base;
            System.out.println(sub.count);//20
        }
    }
    class Base{
        int count = 10;
    }
    class Sub extends Base{
        int count = 20;
    }
    
  • instanceOf比较操作符,用于判断对象的运行类型是否为xx类型或者XX类型的子类型

    public class PolyDetail3 {
        public static void main(String[] args) {
            BB bb = new BB();
            System.out.println(bb instanceof BB);// true
            System.out.println(bb instanceof AA);//true
    
            AA aa = new BB();
            System.out.println(aa instanceof AA);//true
            System.out.println(aa instanceof BB);//true
        }
    }
    class AA{ }
    class BB extends AA{ }
    
3.java的动态绑定机制[重点]
  • 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  • 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
public class Dynamic {
    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.sum());//40
        System.out.println(a.getl());//30
        
        //注释 B类中的sum()和sum1()方法
        System.out.println(a.sum1());//30
        System.out.println(a.getl());//20
    }
}
class A{
    public int i = 10;

    //动态绑定
    public int sum(){
        return getl() + 10;
    }
    public int sum1(){
        return i + 10;
    }
    public int getl(){
        return i;
    }
}
class B extends  A{
    public int i = 20;
    public int sum(){
        return i + 20;
    }
    public int sum1(){
        return i;
    }
    public int getl(){
        return i + 10;
    }
}
4.多态的应用
public class Employee {
    private String name;
    private double salary;

    public Employee() {
    }

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
    public double getAnnual(double salary){
        return 12 * salary;
    }
}
---------------------------------------------------------------------------------------
public class Worker extends Employee{
    public Worker() {
    }

    public Worker(String name, double salary) {
        super(name, salary);
    }

    public void work(String name){
        System.out.println(name+"在认真工作");
    }
    public double getAnnual(double salary){
        return super.getAnnual(salary);
    }
}
--------------------------------------------------------------------------------------
public class Manager extends Employee{
    private double bonus;

    public Manager() {
    }

    public Manager(String name, double salary, double bonus) {
        super(name, salary);
        this.bonus = bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
    public void manage(String name){
        System.out.println(name+"管理公司正常运行");
    }
    public double getAnnual(double bonus,double salary){
        return super.getAnnual(salary) + bonus;
    }
}
-----------------------------------------------------------------------
public class PloyParamter {
    public static void main(String[] args) {
        Worker worker = new Worker("李四",8000);
        Manager manager = new Manager("张三",15000,50000);
        PloyParamter ployParamter = new PloyParamter();
        ployParamter.showEmployeeAnnual(worker);//96000
        ployParamter.showEmployeeAnnual(manager);//180000
        ployParamter.testWork(worker);//李四在认真工作
        ployParamter.testWork(manager);//张三管理公司正常运行
    }
    public  void showEmployeeAnnual(Employee emp){
        System.out.println(emp.getAnnual(emp.getSalary()));
    }
    public void testWork(Employee employee){
        if (employee instanceof Worker){
            ((Worker) employee).work(employee.getName());
        } else if(employee instanceof Manager){
            ((Manager) employee).manage(employee.getName());
        }
    }
}
2.6Super
1.基本介绍
  • super代表父类的引用,用于访问父类的属性、方法、构造器
2.基本语法
  • 访问父类的属性,但不能访问父类private属性 super.属性名; ------就近原则
  • 访问父类的方法,但不能访问父类的private方法 super.方法名([参数列表])
  • 访问父类的构造器 **super(参数列表) **只能放在构造器的第一句,只能出现一句
3.super和this的比较
区别点thissuper
访问属性访问本类的属性,如果本来没有此属性则从父类中继续查找访问父类中的属性
调用方法访问本类中的方法,如果本类没有此方法则从父类继续查找直接访问父类的方法
调用构造器调用本类构造器,必须放在构造器的首行调用父类的构造器,必须放在子类构造器的首行
特殊表示当前对象子类中访问父类对象
2.7overwrite
1.基本介绍
  • 方法覆盖(重写)就是子类有一个方法,和父类的某个方法名称返回类型参数列表一样,那么我们就说子类的这个方法覆盖了父类的那个方法
2.注意事项和使用细节
  • 子类的方法的参数、方法名称,要和父类方法的参数,方法名称完全一样
  • 子类方法的返回类型和父类返回类型一样,或者是父类返回类型的子类
  • 子类方法不能缩小父类方法的访问权限
3.发生覆盖的条件
  • 三同一不低“子类和父类方法名称参数列表返回类型必须相同(注:JDK>= 1.7,返回类型也可以是父类返回值的派生类),子类方法的访问修饰符权限不能比父类
  • 子类方法不能抛出比父类方法更多异常;即子类方法所抛出的异常必须和父类方法所抛出的异常一致,或者是其子类,或者什么也不抛出
  • 被覆盖的方法不能是final类型
  • 被覆盖的方法不能为private
  • 被覆盖的方法不能为static
4.重载和重写的比较
名称发生范围方法名参数列表返回类型修饰符
重写(override)父子类必须一样相同子类重写的方法,返回的类型和父类返回的类型一致,或者为其子类子类方法不能缩小父类方法的访问范围
重载(overload)本类必须一样类型、个数或顺序至少有一个不同无要求无要求
2.8Object类详解
1.equals()方法
  • == 和 equals的对比
    • ==:既可以判断基本类型,又可以判断引用类型
    • ==:如果判断基本类型,判断的是值是否相等
    • ==:如果判断引用类型,判断的是地址是否相等,即判断是不是同一个对象
    • equals: 是Object类中的方法,只能判断引用类型
    • 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
2.hashCode()
  • 提供具有哈希结构的容器效率
  • 两个引用,如果指向的是同一个对象,则哈希值肯定一样的
  • 两个引用,如果指向的是不同对象,则哈希值是不一样的
  • 哈希值主要根据地址号来的! 不能完全将哈希值等价于地址
3.toString()
  • 默认返回: 全类名 + @ +哈希值的十六进制

    //源码
    public String toString() {
       return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    
4.finalize()
  • 当对象被回收时,系统自动调用该对象的finalize方法,子类可以重写该方法,做一些释放资源的操作
  • 什么时候被回收: 当某个对象没有引用时,则jvm就认为这个对象时一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象之前,会先调用finalize方法
  • 垃圾回收机制的调用,是由系统来决定,也可以通过**System.gc()**主动触发垃圾回收机制
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值