008-面向对象高级-1
面向对象高级
final关键字
认识final关键字
-
final可以修饰类、方法、变量。
-
修饰类:该类被称为最终类,特点是不能再被继承了
-
修饰方法,该方法被称为最终方法,特点是不能被重写了
-
修饰变量:该变量必须有且仅能被赋值一次
-
变量有哪些?
a. 成员变量:
静态成员变量
实例成员变量
b. 局部变量
-
修饰局部变量
a. 比如给π这种定值创建一个变量
b. 比如给函数的某几个形参前加上final关键字,防止方法内对该值进行改变
-
修饰静态变量
a. 虽然final放在static前面或者后面都不会报错,但规范是final放在static后面!
b. final修饰静态变量,这个变量今后被称为常量,可以记住一个固定值,并且程序中不能修改了,通常这个值作为系统的配置信息。
c. 常量的名称,建议全部大写,多个单词用下划线连接
-
修饰实例变量
-
final修饰变量注意:
- final修饰基本类型变量,变量存储的数据不能被改变
- final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向对象的内容是可以被改变的
-
-
-
使用场景:工具类等不适于被继承的可以用final修饰
常量
- 使用了static final修饰的成员变量被称为常量。
- 作用:常用于记录系统的配置信息。
- 常量的名称,建议全部大写,多个单词用下划线连接
- 企业中常定义一个Constant类,用于记录常量
- 使用常量记录系统配置信息的优势、执行原理
- 代码可读性更好,可维护性也更好
- 程序编译后,常量会被“宏替换”:初选常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和字面量的性能是一样的。不会每次都会去对那个Constant类进行寻址。
单例类(设计模式)
-
什么是设计模式?
- 一个问题通常有n种解法,其中肯定有一种解法是最优的,这个最优的解法被人总结出来了,称之为设计模式。
- 设计模式有20多种,对应20多种软件开发中会遇到的问题
- 关于设计模式,主要学什么
- 解决什么问题
- 怎么写
-
单例设计模式
-
作用:确保某个类只能创建一个对象
- 例如电脑的任务管理器,只需要一个窗口就可以管理电脑的任务,再次点击不会创建更多的任务管理器窗口
-
怎么写单例类1?(饿汉式单例:拿对象的时候对象已经被创建好了)
public class A { // 2、 定义一个类变量记住类的一个对象 private static A a = new A(); // 1、 构造器私有化 private A() { } // 3、 静态方法返回类对象 public static A getObject() { return a; } public static void main(String[] args) { A a = A.getObject(); } }-
1、 单例必须构造器私有化:确保单例类不能对外创建多个对象,单例才有可能性
-
2、 定义一个类变量记住类的一个对象
public class Test { // 前两步已经实现了单例类但不专业,可以用如下语句创建(假如我的A类是public static A a = new A();而不是private static A a = new A();) A a1 = A.a; A a2 = A.a; // a1、a2是同一个对象,打印地址会发现相同 // 但如果我使用如下代码修改A.a A.a = null; // 那么A唯一的对象就被销毁了 }// 所以在A类中,要把这个唯一的对象定义为私有 private static A a = new A(); // 或用final令其禁止被修改(可进一步防止在类内部修改) public static final A a = new A(); -
3、 静态方法返回类对象
// 若把这个唯一的对象定义为了私有: private static A a = new A(); // 则要定义一个方法让外界调用这个对象 public static A getObject() { return a; }
-
-
怎么写单例类2?(懒汉式单例:用对象时,才开始创建对象)
-
public class B { // 2、 定义一个类变量记住类的一个对象 private static B b;// null // 1、 构造器私有化 private B() { } // 3、 静态方法返回类对象 public static B getObject() { if (b == null) { // 第一次拿对象时会创建对象 b = new B(); } // 后面再创建对象时直接返回第一次创建的那个对象 return b; } public static void main(String[] args) { } }
-
-
枚举类
-
枚举类是一种特殊类
-
枚举类的写法
修饰符 enum 枚举类名 { 名称1, 名称2, ...; 其他成员... }public enum EnumDemo { // 枚举类的第一行:只能罗列枚举类对象的名称, A, B, C; public static void main(String[] args) { System.out.println(EnumDemo.A); } }-
特点:
- 枚举类的第一行,只能写枚举类的对象名称,且要用逗号隔开。这些名称,本质是常量(static final),每个常量都记住了枚举类的一个对象。
- 枚举都是最终类,不可以被继承,枚举类都是继承
java.lang.Enum类的 - 枚举类的构造器都是私有的(写不写都只能是私有),因此,枚举类对外不能创建对象
- 只写一个对象名称就是单例类
-
idea在某些时候反编译较弱,如何查看枚举类反编译后的样子?
-
可以在cmd中把这个类拿出来用javac命令编译这个文件生成对应的.class文件
-
然后再用javap命令反编译这个文件

-
-
编译器为枚举对象新增了几个功能
public enum EnumDemo { X, Y, Z; }public class Test { EnumDemo a1 = EnumDemo.X; // 默认重写了toString()方法 System.out.println(a1);// X EnumDemo a2 = EnumDemo.Y; System.out.println(a2);// Y System.out.println(a1.name());// X System.out.println(a2.name());// Y System.out.println(a1.ordinal());// 索引 System.out.println(a2.ordinal());// 索引 }
-
-
枚举类应用场景
- 枚举类很适合做信息分类和标志,例如可以定义一个移动方向信号,上下左右
- 如果用常量做方向筛选,参数值不受枚举类约束
- 如果用枚举对象,那么传入的值只能是枚举对象内的值,而且方法形参设置传入枚举对象类型后,这个方法内调用枚举内值的时候可以不用枚举类类名点那个值,而直接用那个值的名字。
- 枚举类很适合做信息分类和标志,例如可以定义一个移动方向信号,上下左右
抽象类
-
在Java中有一个关键字交abstract,它就是抽象的意思,可以用它修饰类、成员方法
-
abstract修饰类,这个类就是抽象类
-
abstract修饰方法,这个方法就是抽象方法
public abstract class A { // 抽象方法,必须abstract修饰,只有方法签名,不能有方法体 public abstract void test(); }
-
-
抽象类的注意事项、特点
-
抽象类中不一定有抽象方法,但抽象方法必须在抽象类中
-
类有的成员:成员变量、方法、构造器,抽象类都可以有。
-
抽象类最主要的特点:抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现
- 不能创建对象的原因是抽象类中可以定义抽象方法,而抽象方法没有方法体
-
一个类继承抽象类,必须重写完继承的所有抽象方法,否则这个类也必须定义成抽象类
- 一般在工程中不会让子类也成为抽象类,而是重写抽象父类的全部方法
-
抽象不是必须的,只是它要求子类必须实现其中方法,这样更规范、更严格
-
-
使用抽象类的好处
- 父类指导每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现,我们这样设计抽象类就是为了更好地支持多态。
模板方法设计模式
- 提供一个方法作为完成某类功能的模板,模板方法封装了每个实现步骤,但允许子类提供特定步骤的实现。
- 举例:
- 比如现有老师和学生两个类,两个类中都有写作文这个方法,方法中大部分内容一样但有几行代码不同。
- 对于这种行为方法差异,可以将这个方法抽出来定义一个人类类,然后在人类类中定义写作文方法中相同代码部分。
- 之后把人类类定义成抽象类,并在其中新建抽象方法,然后在写作文方法中调用这个方法。
- 这样老师和学生两个子类就可以通过重写抽象方法来实现差异部分的代码,从而减少冗余。
- 使用final修饰模板方法:人类类中的写作文方法可以加上final,防止子类对它进行修改
接口
接口概述
-
Java提供了一个关键字interface定义出接口
public interface 接口名 { // 成员变量(常量) // 成员方法(抽象方法) }-
传统接口:在jdk8之前接口中只能定义成员变量(常量)和成员方法(抽象方法)
-
常量规范大写加下划线
-
接口中的成员变量默认就是常量
-
可以省略
public static final不写,默认会加上去 -
常量需要再定义时就赋值
String SCHOOL_NAME = "CQUPT"; // 相当于 public static final String SCHOOL_NAME = "CQUPT";
-
-
接口中成员方法默认就是抽象方法
-
可以省略
public abstract不写,默认会加上去void run(); // 相当于 public abstract void run(); String go(); // 相当于 public abstract String go();
-
-
接口不能创建对象!
-
接口时用来被实现(implements)的,实现接口的类被称为实现类,一个类可以同时实现多个接口【可以理解为亲爹(继承)只能有一个,而干爹(实现)可以有多个】
-
实现类:
// 实现类 修饰符 class 实现类类名 implemrnts 接口1, 接口2, 接口3,... { // 实现类实现多个接口,必须重写完全部接口的全部抽象方法,否则实现类需要定义成抽象类!!! }
-
-
接口的好处
- 弥补了类单继承的不足,一个类同时可以实现多个接口,使类拥有更多的角色,功能更强大。
- 例如:
- 如果Teacher类继承了People类实现了Driver接口和BoyFriend接口,那么就可以把一个新建的Teacher对象赋给Driver或者BoyFriend或者People。
- 接口可以实现面向接口编程,更利于解耦合。
- 例如:
jdk8、jdk9开始接口新增的三种方法
-
默认方法(jdk8开始支持)
-
默认方法(普通实例方法),必须加
default修饰,默认会用public修饰,可以省略publicdefault void go() { System.out.println("==go方法执行了==") // ... } -
如何调用默认方法?
- 创建实现类,用实现类调用默认方法
-
-
私有方法(jdk9开始支持)
-
私有方法(私有的实例方法)
-
使用接口中其他实例方法(默认方法)来调用它
default void go() { System.out.println("==go方法执行了==") // 私有方法无法在外部调用,但可以在接口内部的其他实例方法中调用 run(); } // 定义私有方法 private void run() { System.out.println("==run方法执行了==") }
-
-
静态方法
-
默认会用
public修饰,可以省略publicstatic void show() { System.out.println("==show方法执行了==") } -
如何调用静态方法?
-
只能使用当前接口名调用
// 假设这个接口名是A,则调用静态方法如下: A.show(); -
在继承中,子类名可以用来调用父类的静态方法
-
在实现中,实现类名无法调用接口中的静态方法,只能使用接口名调用
-
-
- 新增三种方法,增强了接口的能力,更便于项目的扩展和维护
- 接口变得更像抽象类
- 应用场景:如果项目要求新增一个功能,有新增的方法就可以直接在接口里新增这个功能,由于本质是实例方法,所以不需要实现类去重写,这样就可以避免在所有实现该接口的所有实现类中都再多重写一个方法。
接口的注意事项
-
接口和接口可以多继承,一个接口可以同时继承多个接口!
- 类与类:单继承,一个类只能继承一个直接父类
- 类与接口:多实现,一个类可以同时实现多个接口
- 接口与接口:多继承,一个接口可以同时继承多个接口
- 场景:有时需要实现A、B、C三个接口,此时可以让C继承A和B,这样就只用实现一个C接口。
-
一个接口继承多个接口时,如果多个接口存在方法签名冲突(方法名前面返回的类型不同但方法名相同),则此时不支持多继承,也不支持多实现。
- 如果返回值类型和方法名都一样,会把两个方法合并成一个
-
一个类继承了父类,又同时实现了接口,如果父类中和接口中存在同名的默认方法,实现类会优先用父亲的
// 假设接口名为A2,父类名为Animal,其中均存在show方法 // 现有Dog类: class Dog extends Animal implements A2 { public void go() { show(); // 默认使用父类 super.show(); // 调用父类的 A2.super.show(); // 指定是A2,调用A2接口的方法 } } -
一个类实现了多个接口,如果多个接口中存在同名的默认方法,可以不冲突,这个类重写该方法即可。
- 就是实现的两个类中都有show方法的时候,此时产生歧义,那么只需要在实现类中重写show方法即可,如果要调用实现的接口的中方法,可以使用
接口.super.show();来调用对应接口的方法
- 就是实现的两个类中都有show方法的时候,此时产生歧义,那么只需要在实现类中重写show方法即可,如果要调用实现的接口的中方法,可以使用
抽象类和接口的区别对比
-
相同点
- 都是抽象形式,都可以有抽象方法,都不能创建对象
- 都是派生子类形式:抽象类是被子类继承使用,接口是被实现类实现
- 一个类继承抽象类或实现接口,都必须重写完他们的抽象方法,否则自己要成为抽象类或者报错!
- 都能支持多态,都能够实现解耦合
-
不同点
-
抽象类中可以定义类的全部普通成员;接口只能定义常量、抽象方法、jdk8新增的三种方式
-
抽象类只能被类单继承;接口可以被类多实现
-
最佳实践
-
抽象类体现模板思想:更利于做父类实现代码的复用性。
-
接口更适合做功能的解耦合:解耦合性更强跟不跟灵活。
-
-
杂项
- ctrl+alt+T可以把选中的代码放入for循环中

浙公网安备 33010602011771号