【JAVA语言学习】第08章 面向对象编程(中级部分)(P263 - P361)

韩顺平 零基础30天学会Java 学习笔记

第08章 面向对象编程(中级部分)(P263 - P361)

Eclipse 快捷键:Eclipse 快捷键 | 菜鸟教程 (runoob.com)

8.1 包

  • 包的三大作用

    • 区分相同名字的类
    • 当类很多时,可以很好的管理类
    • 控制访问范围
  • 基本语法

    package com.hspedu;

    • package 关键字,表示打包
    • com.hspedu 表示包名
  • 包的本质,实际上就是创建不同的文件夹/目录来保存类文件

  • 包的命名:只能包含数字、字母、下划线、小圆点。但是不能用数字开头,不能是关键字或保留字。一般是com.公司名.项目名.业务模块名

  • 常用的包

    一个包下,包含很多的类,java 中常用的包有:

    1. java.lang.* //lang 包是基本包,默认引入,不需要再引入.
    2. java.util.* //util 包,系统提供的工具包, 工具类,使用Scanner
    3. java.net.* //网络包,网络开发
    4. java.awt.* //是做java 的界面开发,GUI
  • 包的引入:import

    需要使用到哪个类,就导入哪个类即可,不建议使用*导入

  • package 的作用是声明当前类所在的包,需要放在类(或者文件)的最上面,一个类中最多只有一句package

8.2 访问修饰符(重要)

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

  1. 公开级别:用public 修饰,对外公开
  2. 受保护级别:用protected 修饰,对子类和同一个包中的类公开
  3. 默认级别:没有修饰符号,向同一个包的类公开.
  4. 私有级别:用private 修饰,只有类本身可以访问,不对外公开.
  • 访问范围

    访问级别访问修饰符同类同包子类不同包
    公开public
    受保护protected×
    默认没有修饰符××
    私有private×××
  • 注意事项

    • 修饰符可以用来修饰类中的属性,成员方法和类
    • 只有默认的和public才能修饰类,并且遵循上述访问权限的特点
    • 成员方法的访问规则和属性完全一样

8.3 封装(OPP三大特征之一)

封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作;

  • 好处

    • 隐藏实现细节:方法(连接数据库) <-- 调用(传入参数…)
    • 可以对数据进行验证,保证安全合理
  • 实现步骤

    1. 将属性进行私有化 private(不能直接修改属性)

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

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

      public 数据类型 getXxx(类型 参数名) { // 权限判断,Xxx表示某个属性
      	return xx;
      }
      

8.4 继承(OPP三大特征之一)

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

在这里插入图片描述

  • 基本语法

    class 子类 extends 父类 {}

    • 父类就会自动拥有父类定义的属性和方法
    • 父类又叫超类,基类
    • 子类又叫派生类
  • 优点

    1. 代码的复用性提高了
    2. 代码的扩展性和维护性提高了
  • 注意事项和细节

    • 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
    • 子类必须调用父类的构造器, 完成父类的初始化
    • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器;如果父类没有提供无参构造器,则必须在子类的构造器中用super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。)
    • 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)
    • super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
    • super() 和this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
    • java 所有类都是Object 类的子类, Object 是所有类的基类.
    • 父类构造器的调用不限于直接父类!将一直往上追溯直到Object 类(顶级父类)
    • 子类最多只能继承一个父类(指直接继承),即java 中是单继承机制。
      思考:如何让A 类继承B 类和C 类? 【A 继承B, B 继承C】
    • 不能滥用继承,子类和父类之间必须满足is-a 的逻辑关系
  • 继承本质

    寻找查找关系

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

在这里插入图片描述

  • 练习

在这里插入图片描述

this 和 super不能共存

Super关键字

super 代表父类的引用,用于访问父类的属性、方法、构造器

  • 基本语法

    • 访问父类的属性,但不能访问父类的private属性:super.属性名;
    • 访问父类的方法,不能访问父类的private方法:super.方法名(参数列表);
    • 访问父类的构造器:super(参数列表); (只能放在构造器的第一句,只能出现一句)
  • 细节

    • super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super 去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super 访问遵循就近原则。A->B->C

    • 希望调用父类-A 的cal 方法,这时,因为子类B 没有cal 方法,因此我可以使用下面三种方式。找cal 方法时(cal() 和this.cal()),顺序是:
      (1)先找本类,如果有,则调用
      (2)如果没有,则找父类(如果有,并可以调用,则调用)
      (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object 类
      提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access;如果查找方法的过程中,没有找到,则提示方法不存在

      访问属性同样的规则

    • 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果。

  • super和this的比较

    No.区别点thissuper
    1调用属性访问本类中的属性,如果本类没有此属性,则从父类中继续查找从父类开始查找属性
    2调用方法访问本类中的方法,如果本类没有此方法,则从父类继续查找从父类开始查找方法
    3调用构造器调用本类构造器,必须放在构造器的首行调用父类构造器,必须放在子类构造器的首行
    4特殊表示当前对象子类中访问父类对象
方法重写(Overwrite)

子类有一个方法,和父类的某个方法的名称、返回类型、参数一致,那么就说子类的这个方法覆盖了父类的方法。

  • 注意事项

    • 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样

    • 子类方法的返回类型是和父类返回类型一样 或者是 父类返回类型的子类

      举例:父类返回类型是 Object, 子类返回类型是 String (String是Object的子类)

      父类:public Object getInfo(){} 子类:public String getInfo(){}

    • 子类方法不能缩小父类方法的访问权限(public -> protected -> default -> private)

      父类是protected,而子类是public,可以

      父类是public,而子类是protected,不可以,缩小了

  • 方法重写和方法重载

    名称发生范围方法名形参列表返回类型修饰符
    重载本类必须一样类型,个数、顺序至少有一个不同无要求无要求
    重写父子类必须一样相同子类重写的方法,返回的类型和父类返回的类型一致,或者是其子类子类方法不能缩小父类方法的访问范围

8.5 多态(OPP三大特征之一)

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

  • 举例

    主人给动物喂食,喂食这个动方法可以有很多动物,食物可以有很多,代码复用性不高,不利于代码维护

    而使用多态,就可以统一管理主人喂食问题

    public class Animal {
    	public void cry() {
    		System.out.println("Animal cry() 动物在叫....");
    	}
    }
    -------------------------------------------------------------
    public class Cat extends Animal {
        public void cry() {
        	System.out.println("Cat cry() 小猫喵喵叫...");
        }
    }
    -------------------------------------------------------------
    public class Dog extends Animal {
        public void cry() {
    		System.out.println("Dog cry() 小狗汪汪叫...");
    	}
    }
    -------------------------------------------------------------
    public class PolyObject {
        public static void main(String[] args) {
        	//体验对象多态特点
        	//animal 编译类型就是Animal , 运行类型Dog
        	Animal animal = new Dog();
        	//因为运行时, 执行到改行时,animal 运行类型是Dog,所以cry 就是Dog 的cry
        	animal.cry(); //小狗汪汪叫
        	//animal 编译类型Animal,运行类型就是Cat
        	animal = new Cat();
        	animal.cry(); //小猫喵喵叫
        }
    }
    
  • 多态的具体体现

    • 方法的多态
      • 重载:传入不同的参数,就会调用不同的方法
      • 重写:会自动定位相应的方法
    • 对象的多态
      • 一个对象的编译类型和运行类型可以不一致
      • 编译类型在定义对象时,就确定了,不能改变
      • 运行类型是可以变化的
      • 编译类型看定义时 = 号 的左边,运行类型看 = 号的 右边
  • 多态的前提是:两个对象(类)存在继承关系

  • 多态的向上转型

    • 本质:父类的引用指向了子类的对象
    • 语法:父类类型 引用名 = new 子类类型();
    • 特点:
      • 编译类型看左边,运行类型看右边
      • 可以调用父类中的所有成员(需遵守访问权限)
      • 不能调用子类中特有成员
      • 最终运行效果看子类的具体实现
  • 多态的向下转型

    • 语法:子类类型 引用名 = (子类类型)父类引用;

    • 只能强转父类的引用,不能强转父类的对象

    • 要求父类的引用必须指向的是当前目标类型的对象

    • 当向下转型后,可以调用子类类型中所有的成员

      通过向上转型后,获得不了子类的成员。然后通过向下转型,调用子类成员

public class Animal {
    String name = "动物";
    int age = 10;
    public void sleep(){
    	System.out.println("睡");
    }
    public void run(){
        System.out.println("跑");
    }

    public void eat(){
        System.out.println("吃");
    }
    public void show(){
        System.out.println("hello,你好");
    }
}
----------------------------------------------------------------
public class Cat extends Animal {
    public void eat(){//方法重写
    	System.out.println("猫吃鱼");
    }
    public void catchMouse(){//Cat 特有方法
    	System.out.println("猫抓老鼠");
    }
}
----------------------------------------------------------------
public class Dog extends Animal {//Dog 是Animal 的子类
}
----------------------------------------------------------------
public class PolyDetail {
    public static void main(String[] args) {
	
        // 向上转型: 父类的引用指向了子类的对象
		// 语法:父类类型引用名= new 子类类型();
		Animal animal = new Cat();
		Object obj = new Cat();//可以吗? 可以Object 也是Cat 的父类
        
        //向上转型调用方法的规则如下:
        // (1)可以调用父类中的所有成员(需遵守访问权限)
        // (2)但是不能调用子类的特有的成员
        // (#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
        // animal.catchMouse();错误
        // (4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法
        // ,然后调用,规则我前面我们讲的方法调用规则一致。
        animal.eat();    // 猫吃鱼..
        animal.run();    // 跑
        animal.show();   // hello,你好
        animal.sleep();  // 睡

        // 调用Cat 的catchMouse 方法
        // 向下转型
        // (1)语法:子类类型引用名=(子类类型)父类引用;
        //问一个问题? cat 的编译类型Cat,运行类型是Cat
        Cat cat = (Cat) animal;
        cat.catchMouse();//猫抓老鼠
        // (2)要求父类的引用必须指向的是当前目标类型的对象
        Dog dog = (Dog) animal; //可以吗?
        // 不可以,因为animal本来指向的是猫,所以只能指向猫类
        System.out.println("ok~~");
	}
}
  • 注意事项和细节

    • 属性没有重写之说!属性的值看编译类型
      在这里插入图片描述

    • instanceOf 比较操作符,用于判断对象的运行类型是否为XX 类型或XX 类型的子类型

      System.out.println(bb instanceof BB);

  • 练习
    在这里插入图片描述
    在这里插入图片描述

    属性看编译,方法看运行?

java的动态绑定机制(非常非常重要)

Java 重要特性: 动态绑定机制

  • 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  • 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
    在这里插入图片描述

当注释掉B类的sum方法时,执行A类的sum方法,再执行B类的getI(),所以返回30+10=40。注释掉B类的sum1方法时,执行A类的sum方法,此时 i = 10,所以返回10+10=20。

多态的应用
  • 多态数组

    数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

    public class Person {//父类
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
    	}
    	
    	public String getName() {
    		return name;
    	}
        public void setName(String name) {
        	this.name = name;
        }
        public int getAge() {
        	return age;
        }
        public void setAge(int age) {
        	this.age = age;
        }
        public String say() {//返回名字和年龄
        	return name + "\t" + age;
        }
    }
    -------------------------------------------------------------
    public class Student extends Person {
        private double score;
        public Student(String name, int age, double score) {
            super(name, age);
            this.score = score;
        }
        public double getScore() {
        	return score;
        }
        public void setScore(double score) {
        	this.score = score;
        }
        //重写父类say
        @Override
        public String say() {
        	return "学生" + super.say() + " score=" + score;
        }
        //特有的方法
        public void study() {
        	System.out.println("学生" + getName() + " 正在学java...");
        }
    }
    
    -------------------------------------------------------------
    public class Teacher extends Person {
        private double salary;
        public Teacher(String name, int age, double salary) {
            super(name, age);
            this.salary = salary;
        }
        public double getSalary() {
       		return salary;
        }
        public void setSalary(double salary) {
        	this.salary = salary;
        }
        //写重写父类的say 方法
        @Override
        public String say() {
        	return "老师" + super.say() + " salary=" + salary;
        }
        //特有方法
        public void teach() {
        	System.out.println("老师" + getName() + " 正在讲java 课程...");
        }
    }
    -------------------------------------------------------------
    public class PloyArray {
        public static void main(String[] args) {
            //应用实例:现有一个继承结构如下:要求创建1 个Person 对象、
            // 2 个Student 对象和2 个Teacher 对象, 统一放在数组中,并调用每个对象say 方法
            Person[] persons = new Person[5];
            persons[0] = new Person("jack", 20);
            persons[1] = new Student("mary", 18, 100);
            persons[2] = new Student("smith", 19, 30.1);
            persons[3] = new Teacher("scott", 30, 20000);
            persons[4] = new Teacher("king", 50, 25000);
            //循环遍历多态数组,调用say
            for (int i = 0; i < persons.length; i++) {
            //老师提示: person[i] 编译类型是Person ,运行类型是是根据实际情况有JVM 来判断
                System.out.println(persons[i].say());//动态绑定机制
                //这里大家聪明. 使用类型判断+ 向下转型.
                if(persons[i] instanceof Student) {//判断person[i] 的运行类型是不是Student
                    Student student = (Student)persons[i];//向下转型
                    student.study();
                    //小伙伴也可以使用一条语句((Student)persons[i]).study();
                } else if(persons[i] instanceof Teacher) {
                    Teacher teacher = (Teacher)persons[i];
                    teacher.teach();
                } else if(persons[i] instanceof Person){
                    //System.out.println("你的类型有误, 请自己检查...");
                } else {
                    System.out.println("你的类型有误, 请自己检查...");
                }
            }
        }
    }
    
  • 多态参数

    方法定义的形参类型为父类类型,实参类型允许为子类类型

    package test.ploy;
    
    public class Employee {
    	private String name;
    	private double salary;
    	
    	public Employee(String name, double salary) {
    		super();
    		this.name = name;
    		this.salary = salary;
    	}
    
    	public double getAnnual() {
    		return salary * 12;
    	}
    	
    	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;
    	}
    }
    ---------------------------------------------------------
    package test.ploy;
    
    public class Manager extends Employee {
    	
    	private double bonus;
    	
    	public Manager(String name, double salary, double bonus) {
    		super(name, salary);
    		// TODO Auto-generated constructor stub
    		this.bonus = bonus;
    	}
    	
    	public void manage() {
    		System.out.println("经理 " + getName() + " is working");
    	}
    
    	public double getBonus() {
    		return bonus;
    	}
    
    	public void setBonus(double bonus) {
    		this.bonus = bonus;
    	}
    	
    	@Override
    	public double getAnnual() {
    		return super.getAnnual() + bonus;
    	}
    	
    }   
    -------------------------------------------------------------
    package test.ploy;
    
    public class Worker extends Employee {
    	
    	public Worker(String name, double salary) {
    		super(name, salary);
    		// TODO Auto-generated constructor stub
    	}
    
    	public void work() {
    		System.out.println("普通员工 " + getName() + " is working");
    	}
    	
    	@Override
    	public double getAnnual() {
    		return super.getAnnual();
    	}
    }
    -------------------------------------------------------------
    package test.ploy;
    
    public class Test {
    
    	public static void main(String[] args) {
    		
    		Test t = new Test();
    		Worker tom = new Worker("tom", 2500);
    		Manager milan = new Manager("milan", 5000, 200000);
    		t.showEmpAnnual(tom);
    		t.showEmpAnnual(milan);
    		
    		t.testWork(tom);
    		t.testWork(milan);
    	}
    	
    	public void showEmpAnnual(Employee e) {
    		System.out.println(e.getAnnual());
    	}
    	
    	public void testWork(Employee e) {
    		if(e instanceof Worker) {
    			((Worker) e).work();  // 向下转型
    		} else if(e instanceof Manager){
    			((Manager) e).manage();
    		} else {
    			System.out.println("hhhhhhh");
    		}
    	}
    }
    

8.6 Object类详解,垃圾回收机制

所有类(包括数组)都是先这个类的方法

equals 方法
  • == 和 equals 的对比

    • ==

      是一个比较运算符

      • 既可以判断基本类型,又可以判断引用类型
      • 如果判断基本类型,判断的是值是否相等,,不比较类型
      • 如果判断引用类型,判断的是地址是否相等
    • equals

      • 是Object类中的方法,只能判断引用类型

        equals 方法,源码怎么查看:把光标放在equals 方法,直接输入ctrl+b。如果你使用不了. 自己配置. 即可使用

      • 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如Integer, String

  • 练习
    在这里插入图片描述
    在这里插入图片描述

hashCode() 方法

在这里插入图片描述

  1. 提高具有哈希结构的容器的效率!
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  4. 哈希值主要根据地址号来的!不能完全将哈希值等价于地址。
  5. 后面在集合中hashCode 如果需要的话,也会重写
toString() 方法
  1. 基本介绍,返回对象的字符串表示
    默认返回:全类名+@+哈希值的十六进制
    子类往往重写toString 方法,用于返回对象的属性信息
  2. 重写toString 方法,打印对象或拼接对象时,都会自动调用该对象的toString 形式.
  3. 当直接输出一个对象时, toString 方法会被默认的调用, 比如System.out.println(monster) ; 就会默认调用monster.toString()
finalize() 方法

当垃圾回收器确定不存在该对象的更多引用时,由对象的垃圾回收器调用此方法

  1. 当对象被回收时,系统自动调用该对象的finalize 方法。子类可以重写该方法,做一些释放资源的操作。

  2. 什么时候被回收:当某个对象没有任何引用时,则jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来。销毁该对象,在销毁该对象前,会先调用finalize 方法。

  3. 垃圾回收机制的调用,是由系统来决定(即有自己的GC 算法), 也可以通过System.gc() 主动触发垃圾回收机制

    在实际开发中,几乎不会运用finalize , 所以更多就是为了应付面试.

8.7 断点调试

在断点调试过程中,是运行状态,是以对象的运行类型来执行的。

  • 快捷键
    F7(跳入) F8(跳过) shift+F8(跳出) F9(resume,执行到下一个断点)
    F7:跳入方法内
    F8: 逐行执行代码.
    shift+F8: 跳出方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值