12-面向对象进阶
static
- 含义:static表示静态,是java中的一个修饰符,可以修饰成员方法,成员变量;被static修饰的成员变量叫做静态变量,被修饰的成员方法叫做静态方法
- 特点:被该类所有对象共享;跟对象无关,随着类的加载而加载,优先于对象存在
- 调用方式:类名调用(推荐),对象名调用
- static内存:在堆内存中开辟的静态区,存放static静态变量
- 语法::public static String teacherName;
工具类
-
含义:工具类就是能帮我们做一些事情,但是不描述任何事物的类
-
注意:
- 类名见名知意
- 私有化构造方法
- 方法定义为静态
-
例
package io.xiaoduo.staticPackage; import java.util.ArrayList; public class Utils { private Utils() { } // 打印数组 public static String printArr(int[] arr) { StringBuilder result = new StringBuilder("["); for (int i = 0; i < arr.length; i++) { if (i == arr.length - 1) { result.append(arr[i]); } else { result.append(arr[i]).append(", "); } } result.append("]"); return result.toString(); } // 获取平均分 public static double getAerAge(double[] arr) { double result = 0.0; for (int i = 0; i < arr.length; i++) { result += arr[i]; } return result / arr.length; } // 求集合中最大年龄 public static int getMaxAge(ArrayList<Student> stuList) { int max = 0; for (int i = 0; i < stuList.size(); i++) { if (stuList.get(i).getAge() > max) { max = stuList.get(i).getAge(); } } return max; } }
static的注意事项
- 静态方法只能访问静态变量和静态方法
- 非静态方法可以访问所有,非静态和静态都可以访问
- 静态方法中没有this关键字
- 非静态方法中的this是由虚拟机赋值的,在形参的最前面,是隐藏的,指向当前方法的调用者
继承
- java中提供了一个关键字extends,用这个关键字,可以让一个类和另一个类建立起继承关系
- 语法:public class Student extends Person {}
- Student称为子类(派生类),Person称为父类(基类,超类)
- 什么时候用继承:当类与类之间,存在相同的内容,并满足子类是父类中的一种,就可以考虑使用继承来优化代码
继承的特点
- java只支持单继承,不支持多继承,但支持多层继承
- java中所有的类都直接或间接的继承于Object类
- 子类只能访问父类中非私有的方法
子类到底能继承父类中的哪些内容
构造方法 | 非私有 不能 | private 不能 |
---|---|---|
成员变量 | 非私有 能 | private 能,但是不能直接使用 |
成员方法 | 非私有 能,虚方法表能 | private 不能,static不能,fanal不能 |
- 查看进程id,
jps
- 打开内存分析工具,
jhsdb hsdb
继承中:成员变量的访问特点
- 就近原则: 谁离我近,我就用谁
- 如果顶级对象中还找不到,就报错
- super可以找到父类中的变量;this找当前类的变量
总结:先在局部位置找,父类成员找,逐级向上
继承中:成员方法的访问特点
方法的重写
当父类的方法不能满足子类先在的需求,需要进行方法重写。如果发生了重写会覆盖虚方法表中的方法
书写格式
在继承体系中,子类出现了和父类中一模一样的方法申明,我们就称子类这个方法是重写的方法
@Override重写注解
- @Override是放在重写后的方法上,校验子类重写时语法是否正确
- 加上注解后如果有红色波浪线,表示语法错误
- 建议重写方法都加@Override注解,代码安全优雅
方法重写注意事项
- 重写的方法名称、形参列表必须和父类中的一致
- 子类重写父类方法时,访问权限子类必须大于等于父类(空着不写 < protected < public)
- 子类重写方法时,返回值类型必须小于等于父类
- 建议:重写的方法尽量和父类保持一致
- 只有被添加到虚方法表中的方法才能被重写
继承中:构造方法的访问特点
- 父类中的构造方法不会被子类继承
- 子类中所有的构造方法会默认先访问父类中的无参构造,再执行自己
为什么?
- 子类在初始化的时候有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类中的数据
- 子类初始化之前一定要调用父类构造方法先完成父类数据空间的初始化
怎么调用父类的构造方法
- 子类构造方法第一行语句默认都是super(),不写也存在,是虚拟机帮我们加的,必须在第一行
- 如果想调用父类有参构造,必须手动写super进行调用
this、super使用总结
- this:理解为一个变量,表示当前方法调用者的地址值,就是一个局部变量,可以this(‘1’, 2)这样去访问本类构造方法,虚拟机就不会再添加super了
- super:代表父类储存空间
多态
什么是多态
- 同类型的对象,表现出的不同形态
多态的表现形式
- 父类类型 对象名 = 子类对象;
多态的前提
- 有继承关系
- 有父类引用指向子类对象,相当于把子类对象赋值给父类类型
- 有方法重写
多态的好处
- 使用父类类型作为参数,可以接受所有子类对象
- 体现多态的扩展性与遍历
多态中调用成员的特点
- 变量调用,编译看左边,运行也看左边
- 方法调用,编译看左边,运行看右边
多态的优势和弊端
优势
- 在多态形势下,右边的对象可以实现解耦,便于扩展和维护
- 定义方法的时候,使用父类型作为参数,可以接受所有子类对象,提现多态的扩展性和便利
弊端
- 不能调用子类特有的功能,非覆盖继承的方法,所以说调方法在编译时先去看父类有没有该方法
- 解决:将父类型转为子类类型:Dog d = (Dog)a;
- 细节:转换的时候不能瞎转,如果转换成其他类型就会报错
- instanceof 和js一样, 新特性 if(a instanceof Dog d) { d.lookHome() };如果是变量a,就改为Dog类型的变量d
包
什么是包
- 包就是文件夹,用来管理各种不同功能的
java
类,方便后期代码维护 - 包名的规则:公司域名反写+包的作用,需要全部英文小写,见名知意,
io.xiaoduo.domain
(全类名,全限定名)
使用其他类的规则
不倒包:io.xiaoduo.domain.Student s = new io.xiaoduo.domain.Student();
- 使用同一个包中的类时,不需要导包
- 使用java.lang包中的类时,不需要导包
- 其他情况都要导包
- 如果同时使用两个包中的同类名,需要使用全类名(包名+类名)
final
- 方法:表明该方法是最终方法不能补重写
- 类:表明该类是最终类,不能被继承
- 变量:叫做常量,只能被赋值一次
- 常量的写法:全部大写,单词之间用下划线隔开
- 常量修饰的变量和js一样,基本类型不能改值,引用类型不能改地址
权限修饰符
- 是用来控制一个成员能够被访问的范围
- 可以修饰成员变量,方法,构造方法,内部类
有四种作用范围由小到大(private < 空着不写/默认 < protected < public)
修饰符 | 同一个类中 | 同一个包中 | 不同包下的子类 | 不同包下的无关类 |
---|---|---|---|---|
private | √ | |||
空着不写 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
权限修饰符使用规则
- 成员变量私有
- 方法公开
- 特例:如果方法中的代码是抽取其他方法中共性代码,这个方法一般也私有
代码块
- 局部代码块
- 构造代码块
- 静态代码块
局部代码块(了解)
指的就是写在方法里面的单独大括号,当代码执行到代码块结束的时候,代码块内的变量就被释放了
构造代码块(了解,也被淘汰了)
指的就是写在成员位置的代码块,可以把构造方法中重复的代码抽取到构造代码块中,优先于构造方法执行
静态代码块(掌握)
- 格式:static {}
- 特点:通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
- 使用场景:在类加载时,做数据初始化
- 静态代码块中只能放静态变量
抽象类(abstract)
- 在抽象类中定义的抽象方法,子类必须重写
- public abstract class Person { public abstract void work(); } // 抽象方法不能有主体
抽象类和抽象方法的注意事项
- 抽象类不能实例化
- 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类
- 可以有构造方法,当创建子类对象时,给属性赋值
- 抽象类的子类要么重写抽象类中的所有抽象方法,要么也是抽象类
接口
-
接口用关键字interface定义
public interface 接口名 {}
-
接口不能实例化
-
接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名 {}
-
接口的子类(实现类)
要么重写接口中所有的抽象方法
要么是抽象类
注意1:接口和类的实现关系,可以单实现,也可以多实现
public class 类名 implements 接口名1,接口名2 {}
注意2:实现类还可以再继承的基础上实现多个接口
public class 类名 extends 父类 implements 接口名1,接口名2 {}
接口中成员的特点
- 成员变量
- 只能是常量
- 默认修饰符 public static final
- 构造方法
- 没有
- 成员方法
- 只能是抽象方法
- 默认修饰符:public abstract
- JDK7以前:接口中只能定义抽象方法
- JDK8新特性:接口中可以定义有方法体的方法
- JDK9新特性:接口中可以定义私有方法
接口和类之间的关系
- 类和类是继承关系,只能单继承,不能多继承,但是可以多层继承
- 类和接口是实现关系,可以单实现,也可以多实现,还可以再继承一个类的同时多实现
- 接口和接口是继承关系,可以单继承,也可以多继承,如果实现类实现了最下面的子接口,就需要重写所有的抽象方法
多学三招:接口中新增方法,接口应用和适配器设计模式
JDK8接口中新增的方法
- JDK7以前:接口中只能定义抽象方法
- JDK8新特性:接口中可以定义有方法体的方法(默认和静态)
- JDK9新特性:接口中可以定义私有方法
JDK8接口中新增的方法
- 允许在接口中定义默认方法,需要使用关键字default修饰,作用:解决接口升级的问题
- 接口中默认方法的定义格式
- 格式:public default void show() {};
- 接口中默认方法的注意事项
- 默认方法不是抽象方法,所以不强制被重写,但是如果被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同的默认方法,子类就必须对该方法进行重写
- 允许在接口中定义静态方法,需要static修饰
- 接口中静态方法的定义格式
- 格式:public static void show() {};
- 接口中静态方法的注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
- 不能够被重写
JDK9新增的方法
- 接口中私有方法的定义格式
- 普通私有方法格式:private void show() {};
- 私有静态方法格式:private static void show() {};
接口的应用
- 接口代表规则,是行为的抽象,想要让那个类拥有一个行为,就让这个类实现对应的借口就可以了
- 当一个方法的参数是接口时,可以传递接口所有的实现类的对象,这种方式称之为接口多态
适配器设计模式
- 设计模式(design pattern)是一套被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
- 简单理解:设计模式就是解决各种问题的套路
- 先建一个适配器抽象类对所有的接口方法进行空实现,然后再用对象类继承适配器类,这样就不用重写接口中所有的抽象方法,命名规范xxxAdapter,为了避免其他类创建适配器对象,适配器类应该用abstract修饰
内部类
什么是内部类
- 类的五大成员:属性、方法、构造方法、代码块、内部类
- 在一个类的里面,再定义一个类就叫做内部类
- 内部类表示的事物是外部类的一部分
- 内部单独出现没有任何意义
- 内部类的访问特点
- 内部类可以直接访问外部类中的成员,包括私有
- 外部类要访问内部类的成员必须创建对象
内部类的分类
- 成员内部类:写在成员位置,属于外部类的成员,可以使用外部类编写方法,对外提供内部类对象
- 静态内部类:使用static修饰的内部类,使用外部类
.
内部类创建对象 - 局部内部类:将内部类定义在方法里面就叫做局部内部类,外界无法直接使用,需要在方法内部创建对象使用
匿名内部类(掌握)
-
格式:new 类/接口(){ 重写的方法; };
new Swim() { @Override public void swim() { // sout("重写的游泳方法"); } }
-
类/接口多态:类/接口 对象 = new 类/接口(){ 重写的方法; };
什么是匿名内部类
- 隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式的细节
- 包含了继承或者实现,方法重写,创建对象
- 整体就是一个类的子类对象或者接口的实现类对象
使用场景
- 当方法的参数是接口或者类时
- 以接口为例,可以传递这个接口的实现类对象
- 如果实现类只要使用一次,就可以使用匿名内部类简化代码