Java基础补充--面向对象编程中级部分

面向对象编程中级部分

1、包

包的本质实际上就是创建不同的文件夹/目录来保存类文件,画出示意图
在这里插入图片描述
在这里插入图片描述

2、访问修饰符

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

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

在这里插入图片描述

3、面向对象的三大特征

面向对象编程有三大特征:封装、继承和多态。

3.1、封装

案例:对电视机的操作就是典型的封装,我们只需要知道怎么调换频道即可,并不需要知道电视机的工作原理。
在这里插入图片描述在这里插入图片描述

3.2、继承

为什么需要继承:提高代码的复用性
在这里插入图片描述

3.2.1、继承的优点

  1. 代码的复用性提高了
  2. 代码的扩展性和维护性提高了

3.2.2、继承的细节

1、子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问。

将父类提供一个public的方法,来调用n4
private int n4 = 400;

public int getN4() {
    
    
	return n4;
	}
=======================
//test400();错误
//要通过父类提供公共的方法去访问
System.out.println("n4=" + getN4());

2、子类必须调用父类的构造器, 完成父类的初始化

父类中
public Base() {
    
     //无参构造器
System.out.println("父类Base()构造器被调用....");
}

子类中
public Sub() {
    
    //无参构造器
//super(); //默认调用父类的无参构造器
super("smith", 10);
System.out.println("子类Sub()构造器被调用....");
}

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

4、如果希望指定去调用父类的某个构造器,则显式的调用一下: super(参数列表)

5、super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)

【super本意是将父类的构造器执行完毕,然后再执行子类的构造器】

6、super() 和this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

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

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

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

3.2.3、继承的本质

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
根据查找关系来返回信息
在这里插入图片描述

3.2.4 案例测试

在这里插入图片描述
首先分析一下:B b = new B() -----> 到B的无参构造器 ----> 然后根据this ---->B(String name)---->在B中,有一个默认的super ---->因此就到了A() ---->输出a ---->输出 b name —>输出 b
在这里插入图片描述

3.3、Super关键字

super 代表父类的引用,用于访问父类的属性、方法、构造器
在这里插入图片描述
在这里插入图片描述
tips: this指向对象、super指向父类

重点:方法和属性是一致的:this先找本类,再找父类;super是直接查找父类

找cal 方法时(cal() 和this.cal()),顺序是:
(1)先找本类,如果有,则调用
(2)如果没有,则找父类(如果有,并可以调用,则调用)
(3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到Object 类
提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access
如果查找方法的过程中,没有找到,则提示方法不存在
super.cal() —>找cal 方法(super.call()) 的顺序是直接查找父类,其他的规则一样

![在这里插入图片描述](https://img-blog.csdnimg.cn/550f8a41e0df42faa2bd70a28aeab5b7.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56CU6KGM56yU5b2V,size_20,color_FFFFFF,t_70,g在这里插入图片描述
当前阶段
在这里插入图片描述

3.4、方法重写/覆盖(override)

在这里插入图片描述

3.4.1、方法重写的细节

在这里插入图片描述

3.4.2、重载和重写的区别

在这里插入图片描述

3.5、多态

3.5.1、引出

传统的方法带来的问题是什么? 如何解决?
问题是: 代码的复用性不高,而且不利于代码维护
解决方案: 引出我们要讲解的多态

基本概念:同一方法可以根据发送对象不同而采用多种不同的行为方式

3.5.2、方法的多态【重载和重写就体现多态】

public class PloyMethod {
    
    
public static void main(String[] args) {
    
    
		//方法重载体现多态
		A a = new A();
		//这里我们传入不同的参数,就会调用不同sum 方法,就体现多态
		System.out.println(a.sum(10, 20));
		System.out.println(a.sum(10, 20, 30));

		//方法重写体现多态
		B b = new B();
		a.say();
		b.say();

	}
}
class B {
    
     //父类
	public void say() {
    
    
		System.out.println("B say() 方法被调用...");
	}
}

class A extends B {
    
    //子类
	public int sum(int n1, int n2){
    
    //和下面sum 构成重载
		return n1 + n2;
	}
	public int sum(int n1, int n2, int n3){
    
    
		return n1 + n2 + n3;
	}
	public void say() {
    
    
		System.out.println("A say() 方法被调用...");
	}
}


3.5.3、对象的多态 【重难点】

在这里插入图片描述
编译类型在等号左边,运行状态在等号的右边

举例子
首先定义三个类
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(); //小猫喵喵叫
	}
}

多态的前提是存在继承关系

多态的向上转型

在这里插入图片描述

代码实例
父类
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,你好");
	}
}

子类 cat
public class Cat extends Animal {
    
    
	public void eat(){
    
    //方法重写
		System.out.println("猫吃鱼");
	}
	public void catchMouse(){
    
    //Cat 特有方法
		System.out.println("猫抓老鼠");
	}
}

主函数 main
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是指向cat对象
		System.out.println("ok~~");
	}
}
多态的向下转型

向下转型是要在向上转型的基础之上的
在这里插入图片描述
其中补充内容:
2、父类对象是固定的,不能进行修改,只能强制修改父类的引用(举例:人对应的名字可以修改,但是对于本人而言就是你本人,是不能变化的)
3、就是上述的 Animal animal = new Cat();必须有父类的引用指向了当前目标的对象
4、当向下转型后,可以调用子类类型中所有成员;经过了强制转换Cat cat = (Cat) animal;。那么对应的cat.catchMouse();也是可以直接调用了。

属性没有重写之说

属性的值看编译类型,主要就是看编译类型,也就是new对象等号左边的

public class PolyDetail02 {
    
    
	public static void main(String[] args) {
    
    
		//属性没有重写之说!属性的值看编译类型
		Base base = new Sub();//向上转型
		System.out.println(base.count);// ? 看编译类型10
		Sub sub = new Sub();
		System.out.println(sub.count);//? 20
	}
}
class Base {
    
     //父类
	int count = 10;//属性
}
class Sub extends Base {
    
    //子类
	int count = 20;//属性
}
instanceOf 比较操作符

用于判断对象的运行类型是否为XX 类型或XX 类型的子类型

多态练习题

在这里插入图片描述
始终注意:访问属性看编译类型,访问方法看运行类型

首先Sub s = new Sub() 中s 的编译类型是Sub的,因此s.count = 20
s.display 中运行类型是Sub ,因此 this.count = 20
Base b = s 中,将b也指向s的地址,b ==s 是真
下一步b的编译类型是Base ,因此 b.count = 10
b的运行类型是Sub ,b.display 为20

java的动态绑定机制

在这里插入图片描述
当进行修改后,将子类中sum()和sum1()注释掉后

分析题目之前需要知道编译类型和运行类型,然后进行下一步操作

public class DynamicBinding {
    
    
	public static void main(String[] args) {
    
    
		//a 的编译类型A, 运行类型B
		A a = new B();//向上转型
		System.out.println(a.sum());//?40 -> 30
		System.out.println(a.sum1());//?30-> 20
	}
}
class A {
    
    //父类
	public int i = 10;
	//动态绑定机制:getI() 方法和该对象的内存地址/运行类型绑定
	public int sum() {
    
    //父类sum()
		return getI() + 10;//20 + 10
	}
	
	public int sum1() {
    
    //父类sum1(),i为调用属性,没有动态绑定机制,哪里声明,哪里使用
		return i + 10;//10 + 10
	}
	public int getI() {
    
    //父类getI
		return i;
	}
}

class B extends A {
    
    //子类
	public int i = 20;
	// public int sum() {
    
    
	// return i + 20;
	// }
	public int getI() {
    
    //子类getI()
		return i;
	}
	// public int sum1() {
    
    
	// return i + 10;
	// }
}

3.6、Object类详解

3.6.1、equals方法

在这里插入图片描述
练习题
字符串将equal重写了,可以直接判断内容是否相同
在这里插入图片描述
在这里插入图片描述

3.6.2、hashcode方法

在这里插入图片描述

3.6.3、toString方法

默认返回:全类名+@+哈希值的十六进制
全类名:包名和类名 通过getClass().getName()获得
子类往往重写toString 方法,用于返回对象的属性信息【用快捷键alt + insert -->toString】
在这里插入图片描述
在这里插入图片描述

3.6.4、finalize方法

在这里插入图片描述
在这里插入图片描述

3.7 高频考点

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_36317312/article/details/123477874