7 面向对象(中级)
文章目录
7.0 IDEA
-
常用快捷键:
- command + d 删除当前行
- command + option + 向下光标 向下复制当前行
- option + / 补全代码
- command + / 注释
- option + enter 导入该行的类
- command + option + L 格式化代码
- control + R 运行
- optio + A 构造器
- control + H 查看继承关系
- command + B 定位方法
- .var 自动变量名
- Command + option + T 环绕方式
-
常用模版快捷键
- fori 遍历
- itar 遍历数组
- iter 增强遍历
- sout 打印换行
7.1 包
-
基本语法:
Package 包名
-
包的命名:
- 只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
-
命名规范:
- 小写字母+小圆点一般是 com.公司名.项目名.业务模块名
-
引入包:
- import java.uti Scanner;就只是引入一个类Scanner
- import java.util*:1/ 表示将java.util 包所有都引入
-
注意事项和细节:
- package 的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一个package
- import指令 位置放在package的下面,在类定义前面,可以有多句目没有顺序要求
7.2 访问修饰符
-
基本介绍:
java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围)
-
访问权限⭐️⭐️:
-
公开级别:用public修饰,对外公开
-
受保护级别:用protected修饰,对子类和同一个包中的类公开
-
默认级别:没有修饰符号,向同一个包的类公开
-
私有级别:用private修饰,只有类本身可以访问,不对外公开
访问级别 访问修饰符 同类 同包 子类 不同包 公开 public ✅ ✅ ✅ ✅ 受保护 protected ✅ ✅ ✅ ❌ 默认 无 ✅ ✅ ❌ ❌ 私有 private ✅ ❌ ❌ ❌
-
-
注意事项和细节:
- 修饰符可以用来修饰类中的属性,成员方法以及类
- 只有默认的和public才能修饰类,并目遵循上述访问权限的特点
- 子类待定
- 成员方法的访问规则和属性完全样
7.3 封装⭐️
-
基本介绍:
封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作方法封装在一起数据被保护在内部,程序的其它部分只有通过被授权的操作(方法),才能对数据进行操作
-
封装步骤:
- 将属性进行私有化private[不能直接修改属性]
- 提供一个公共的(public)set方法,用于对属性判断井赋值
public void setxxx(类型 参数名){
加入数据验证的业务逻辑;
属性 = 参数名;} - 提供个公共的(public)get方法,用于获取属性的值
public 数据类型 getXxxx(){
return XX;}
7.4 继承⭐️
-
基本介绍:
继承可以解决代码复用,当多个类存在相同的属性(变量)和方法时,可以以这些类中抽象出交类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可
-
基本语法:
class 子类 extends 父类{}
-
注意事项和细节:
-
子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
-
子类必须调用父类的构造器,完成父类的初始化
-
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
-
指定去调用父类的某个构造器,则显式的调用一下:super(参数列表)
-
super在使用时,必须放在构造器第一行(super只能在构造器中使用)
-
super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
-
java所有类都是Object类的子类
-
父类构造器的调用不限于直接父类!将一直往上追潮直到Object类
-
子类最多只能继承一个父类(指直接继承),即java中是单继承机制
-
不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
public class TopBase { //父类是Object public TopBase() { //super(); Object的无参构造器 System.out.println("构造器TopBase() 被调用...");//1 } }
public class Base extends TopBase { //父类 //4个属性 public int n1 = 100; protected int n2 = 200; int n3 = 300; private int n4 = 400; public Base() { //无参构造器 System.out.println("父类Base()构造器被调用...."); } public Base(String name, int age) {//有参构造器 //默认super() System.out.println("父类Base(String name, int age)构造器被调用...."); } public Base(String name) {//有参构造器 System.out.println("父类Base(String name)构造器被调用...."); } //父类提供一个public的方法,返回了n4 public int getN4() { return n4; } public void test100() { System.out.println("test100"); } protected void test200() { System.out.println("test200"); } void test300() { System.out.println("test300"); } private void test400() { System.out.println("test400"); } //call public void callTest400() { test400(); } }
/输入ctrl + H 可以看到类的继承关系 public class Sub extends Base { //子类 public Sub(String name, int age) { //1. 调用父类的无参构造器, 如下或者 什么都不写,默认就是调用super() //super();//父类的无参构造器 //2. 调用父类的 Base(String name) 构造器 //super("hsp"); //3. 调用父类的 Base(String name, int age) 构造器 super("king", 20); //细节:super在使用时,必须放在构造器第一行 //细节: super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器 //this() 不能再使用了 System.out.println("子类Sub(String name, int age)构造器被调用...."); } public Sub() {//无参构造器 //super(); //默认调用父类的无参构造器 super("smith", 10); System.out.println("子类Sub()构造器被调用...."); } //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器 public Sub(String name) { super("tom", 30); //do nothing... System.out.println("子类Sub(String name)构造器被调用...."); } public void sayOk() {//子类方法 //非私有的属性和方法可以在子类直接访问 //但是私有属性和方法不能在子类直接访问 System.out.println(n1 + " " + n2 + " " + n3); test100(); test200(); test300(); //test400();错误 //要通过父类提供公共的方法去访问 System.out.println("n4=" + getN4()); callTest400();// } }
-
-
继承本质:
子对象创建完成,建立查找关系
-
继承内存图⭐️:
7.5 多态⭐️⭐️
7.5.1多态
-
基本介绍:
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础之上的
-
多态具体体现
- 方法的多态
- 重写和重载就是体现多态
- 对象的多态
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时 = 号 的左边,运行类型看 = 号的 右边
- 方法的多态
-
注意事项和细节:
-
向上转型
- 本质:父类的引用指向了子类的对象
- 语法:父类类型 引用名 = new 子类类型();
- 特点:编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(需遵守访问权限)
- 不能调用子类特有成员
- 最终运行效果看子类的具体实现
-
向下转型
- 本质:把指向子类对象的父类引用,转成子类对象的子类引用
- 语法:子类类型 引用名 = (子类类型) 父类引用;
- 只能强转父类引用,不能强转父类对象
- 父类的引用必须指向的是当前目标类型的对象
- 当向下转型后,可以调用子类类型中所有的成员
-
属性
-
属性没有重写,属性的值看编译类型
-
instance of 比较较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型
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; //可以吗? System.out.println("ok~~"); } }
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;//属性 }
public class PolyDetail03 { 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, 运行类型是BB //BB是AA子类 AA aa = new BB(); System.out.println(aa instanceof AA); System.out.println(aa instanceof BB); Object obj = new Object(); System.out.println(obj instanceof AA);//false String str = "hello"; //System.out.println(str instanceof AA); System.out.println(str instanceof Object);//true } } class AA {} //父类 class BB extends AA {}//子类
-
-
7.5.2 动态绑定机制⭐️⭐️
-
当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
-
当调用对象属性时,没有动态綁定机制,哪里声明,哪里使用()
public class DynamicBinding { public static void main(String[] args) { A a = new B(); System.out.println(a.sum()); System.out.println(a.sum1()); } } class A { public int i = 10; public int sum() { return getI() + 10;//当子类不存在sum方法时,会从父类找到sum方法,但动态绑定机制会找到子类的getI返回i值 } public int sum1() { return i + 10; } public int getI() { return i; } } class B extends A { public int i = 20; public int sum() { return i + 20; } public int sum1() { return i + 10; } public int getI() { return i; } }
7.5.3 多态的应用
7.6 Super
-
基本介绍:
super代表父类的引用,用于访问父类的属性、方法、构造器
-
基本语法:
- 访问父类的属性,但不能访问父类的private属性
super.属性名; - 访问父类的方法,不能访问父类的private方法
super.方法名(参数列表); - 访问父类的构造器(只能放在构造器的第一句,只能出现一句)
super(参数列表);
- 访问父类的属性,但不能访问父类的private属性
-
注意事项和细节:
- 调用父类的构造器的好处 (分工明确,父类属性由父类初始化,子类的属性由子类初始化)
- 当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super,如果没有重名,使用super、this、 直接访问效果相同
- super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则
-
super和this
-
实例:
public class Base { //父类是Object public int n1 = 999; public int age = 111; public void cal() { System.out.println("Base类的cal() 方法..."); } public void eat() { System.out.println("Base类的eat()....."); } }
public class A extends Base{ //4个属性 //public int n1 = 100; protected int n2 = 200; int n3 = 300; private int n4 = 400; public A() {} public A(String name) {} public A(String name, int age) {} // public void cal() { // System.out.println("A类的cal() 方法..."); // } public void test100() { } protected void test200() { } void test300() { } private void test400() { } }
public class B extends A { public int n1 = 888; //编写测试方法 public void test() { //super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员; // 如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C System.out.println("super.n1=" + super.n1); super.cal(); } //访问父类的属性 , 但不能访问父类的private属性 [案例]super.属性名 public void hi() { System.out.println(super.n1 + " " + super.n2 + " " + super.n3 ); } public void cal() { System.out.println("B类的cal() 方法..."); } public void sum() { System.out.println("B类的sum()"); //希望调用父类-A 的cal方法 //这时,因为子类B没有cal方法,因此我可以使用下面三种方式 //找cal方法时(cal() 和 this.cal()),顺序是: // (1)先找本类,如果有,则调用 // (2)如果没有,则找父类(如果有,并可以调用,则调用) // (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object类 // 提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access // 如果查找方法的过程中,没有找到,则提示方法不存在 //cal(); this.cal(); //等价 cal //找cal方法(super.call()) 的顺序是直接查找父类,其他的规则一样 //super.cal(); //演示访问属性的规则 //n1 和 this.n1 查找的规则是 //(1) 先找本类,如果有,则调用 //(2) 如果没有,则找父类(如果有,并可以调用,则调用) //(3) 如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object类 // 提示:如果查找属性的过程中,找到了,但是不能访问, 则报错, cannot access // 如果查找属性的过程中,没有找到,则提示属性不存在 System.out.println(n1); System.out.println(this.n1); //找n1 (super.n1) 的顺序是直接查找父类属性,其他的规则一样 System.out.println(super.n1); } //访问父类的方法,不能访问父类的private方法 super.方法名(参数列表); public void ok() { super.test100(); super.test200(); super.test300(); //super.test400();//不能访问父类private方法 } //访问父类的构造器(这点前面用过):super(参数列表);只能放在构造器的第一句,只能出现一句! public B() { //super(); //super("jack", 10); super("jack"); } }
7.7 方法重写(overwrite)
-
基本介绍:
子类的方法和父类方法一致,那么子类的方法覆盖了父类的方法
-
注意事项和细节:
- 子类的方法的形参列表,方法名称,要和父类方法的形参列表,方法名称完全一样
- 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
- 子类方法大于等于父类方法的访问权限
7.8 Object类
-
==
-
既可以判断基本类型,又可以判断引用类型
-
如果判断基本类型,判断的是值是否相等
-
如果判断引用类型,判断的是地址是否相等,即判定是不是同一个对象
public class Equals01 { public static void main(String[] args) { A a = new A(); A b = a; A c = b; System.out.println(a == c); B bObj = a; System.out.println(bObj == c); } } class A extends B{} class B{}
-
-
equals
- equals:是Object类中的方法,只能判断引用类型
- 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等
-
hashCode
- 提高具有哈希结构的容器的效率
- 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的
- 两个引用,如果指向的是不同对象,则哈希值是不一样的
- 哈希值主要根据地址号来的,不能完全将哈希值等价地址
- 后面在集合中hashCode 如果需要的话,也会重写
-
toString
- 默认返回:全类名+@+哈希值的十六进制
- 子类往往重写to String方法,用于返回对象的属性信息
- 重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式
- 当直接输出一个对象时,toString 方法会被默认的调用
-
finalize
- 当对象被回收时,系统自动调用该对象的finalize方法
- 子类可以重写该方法,做一些释放资源的操作
- 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
- 垃圾回收机制的调用,是由系统来決定(即有自己的GC算法),也可以通过System.gc() 主动触发垃圾回收机制