文章目录
面向对象编程中级部分
1、包
包的本质实际上就是创建不同的文件夹/目录来保存类文件,画出示意图
2、访问修饰符
java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
- 公开级别:用public 修饰,对外公开
- 受保护级别:用protected 修饰,对子类和同一个包中的类公开
- 默认级别:default 没有修饰符号,向同一个包的类公开.
- 私有级别:用private 修饰,只有类本身可以访问,不对外公开.
3、面向对象的三大特征
面向对象编程有三大特征:封装、继承和多态。
3.1、封装
案例:对电视机的操作就是典型的封装,我们只需要知道怎么调换频道即可,并不需要知道电视机的工作原理。
3.2、继承
为什么需要继承:提高代码的复用性
3.2.1、继承的优点
- 代码的复用性提高了
- 代码的扩展性和维护性提高了
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()) 的顺序是直接查找父类,其他的规则一样

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方法