抽象类
- 概述: 使用abstract关键字修饰的类就是抽象类
- 特点: 这种类不能被创建对象,它就是用来做父类的,被子类继承的
格式
修饰符 abstract class 类名{
}
抽象类中的成员
- 成员变量
- 成员方法
- 构造方法
- 抽象方法
抽象方法: 没有方法体,使用abstract修饰的方法就是抽象方法
修饰符 abstract 返回值类型 方法名(形参列表);
例如:
public abstract void work();
抽象方法的作用: 强制要求子类重写的
- 抽象方法: 没有方法体,使用abstract修饰的方法就是抽象方法
- 抽象方法定义格式: 修饰符 abstract 返回值类型 方法名(形参列表);
- 使用场景:如果父类中某个方法,所有子类都有不同的实现,那么就可以把该方法定义为抽象方法
- 抽象方法的作用: 强制要求子类重写
package com.company.day06.chouxianglei;
// 抽象类
public abstract class Animal {
private String name;
private int age;
public Animal() {
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
// 抽象方法
public abstract void eat();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗狗吃肉");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.eat();
Dog dog = new Dog();
dog.eat();
}
}
抽象类的注意事项
- 抽象类不能被创建对象,就是用来做“父类”,被子类继承的。
- 抽象类不能被创建对象,但可以有“构造方法”——为成员变量初始化。
- 抽象类中可以没有抽象方法,但抽象方法必须定义在抽象类中
- 子类继承抽象类后,必须重写抽象类中所有的抽象方法,否则子类必须也是一个抽象类
匿名类
new Animal() {
@Override
public void eat() {
System.out.println("匿名动物吃");
}
}.eat();
模板设计模式
设计模式就是解决一些问题时的固定思路,也就是代码设计思路经验的总结。
针对某些情况,在父类中指定一个模板,然后根据具体情况,在子类中灵活的具体实现该模板
public abstract class Person{
// 有方法体的方法: 通用模板
public void sleep(){
System.out.println("两眼一闭,就睡觉...");
}
// 没有方法体的方法(抽象方法): 填充模板(要子类重新实现的)
public abstract void eat();
}
抽象类体现的就是模板设计思想,模板是将通用的东西在抽象类中具体的实现,而模板中不能决定的东西定义成抽象方法,让使用模板(继承抽象类的类)的类去重写抽象方法实现需求
模板模式的实现步骤
- 定义抽象父类作为模板
- 在父类中定义"模板方法"— 实现方法(通用模板)+抽象方法(填充模板)
- 子类继承父类,重写抽象方法(填充父类的模板)
- 测试类: -创建子类对象,通过子类调用父类的“实现的方法”+ “子类重写后的方法”
案例
假如我现在需要定义新司机和老司机类,新司机和老司机都有开车功能,开车的步骤都一样,只是驾驶时的姿势有点不同,新司机:开门,点火,双手紧握方向盘,刹车,熄火,老司机:开门,点火,右手握方向盘左手抽烟,刹车,熄火。那么这个时候我们就可以将固定流程写到父类中,不同的地方就定义成抽象方法,让不同的子类去重写
分析:
- 司机类
- 开车方法: 确定实现–通用模板
- 开门
- 点火
- (姿势)
- 刹车
- 熄火
- 姿势方法: 不确定实现–填充模板
- 开车方法: 确定实现–通用模板
- 新司机类继承司机类,重写姿势方法
- 老司机类继承司机类,重写姿势方法
public abstract class Driver {
// 开车方法 通用模板
public void driveCar(){
System.out.println("开门");
System.out.println("点火");
// 姿势??
ziShi();
System.out.println("刹车");
System.out.println("熄火");
}
// 姿势方法 填充模板
public abstract void ziShi();
}
public class NewDriver extends Driver{
@Override
public void ziShi() {
System.out.println("双手紧握方向盘");
}
}
public class OldDriver extends Driver{
@Override
public void ziShi() {
System.out.println("右手握方向盘左手抽烟");
}
}
public class Test2 {
public static void main(String[] args) {
NewDriver newDriver = new NewDriver();
newDriver.driveCar();
OldDriver oldDriver = new OldDriver();
oldDriver.driveCar();
}
}
final关键字
final关键字的概述
final: 不可改变。可以用于修饰类、方法和变量。
- 类:被修饰的类,不能被继承 比如String。
- 方法:被修饰的方法,不能被重写。
- 变量:被修饰的变量,就只能赋值一次,不能被重新赋值。
修饰类
修饰符 final class 类名 {
}
例如:
public final class FinalClassFu {
}
public class FinalClassZi /*extends FinalClassFu*/ {
// FinalClassFu类被final修饰了,所以不能被继承
}
查询API发现像 public final class String 、public final class Math 、public final class Scanner 等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。
修饰方法
修饰符 final 返回值类型 方法名(参数列表){
//方法体
}
重写被 final修饰的方法,编译时就会报错。
public class FinalMethodFu {
public final void show(){
}
}
public class FinalMethodZi extends FinalMethodFu {
/*@Override
public void show() {
}*/
// 无法重写父类中的show方法,因为父类中的show方法被final修饰了
}
修饰变量
局部变量——基本类型
基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。代码如下:
final int NUM = 10;
NUM = 220; // 报错
局部变量——引用类型
引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改,代码如下:
public class Test3 {
public static void main(String[] args) {
final Student student1 = new Student("eric", 11);
System.out.println(student1);
// student1 = new Student("bob", 20); 不能有新的指向
student1.setAge(30);
System.out.println(student1.getAge());
System.out.println(student1);
}
}
成员变量涉及到初始化的问题,初始化方式有两种,只能二选一:
public class FinalVariable {
final int NUM1 = 10;
}
public class FinalVariable {
final int NUM2;
public FinalVariable(int NUM2){
this.NUM2 = NUM2;
}
public FinalVariable(){
this.NUM2 = 10;
}
}
被final修饰的常量名称,一般都有书写规范,所有字母都大写
- final修饰类,类不能被继承。
- final修饰方法,方法不能被重写。
- final修饰变量,变量不能被改值,只能赋值一次.
static关键字
static是一个静态修饰符关键字,表示静态的意思,可以修饰成员变量和成员方法以及代码块。
static修饰成员变量
当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作
static 数据类型 变量名;
// 调用
对象名.静态成员变量名; 不推荐
类名.静态成员变量名; 推荐
public class Person {
String name;
static int id;
public Person(String name) {
this.name = name;
++id;
}
public static void main(String[] args) {
System.out.println(Person.id);
Person p1 = new Person("张三");
Person p2 = new Person(";李四");
Person p3 = new Person(";李四");
System.out.println(Person.id); //3
System.out.println(p3.id);
}
}
静成员会随着类的加载而加载, 并且只加载一次, 会加载到静态区
static修饰成员方法
被static修饰的方法会变成静态方法,也称为类方法,该静态方法可以使用类名直接调用。
修饰符 static 返回值类型 方法名 (参数列表){
// 执行语句
}
访问
对象名.方法名(实参);
类名.方法名(实参); 推荐
package com.company.day06.chouxianglei;
import java.util.Arrays;
public class Person {
String name;
static int id;
public Person(String name) {
this.name = name;
++id;
}
public static void m1(){
System.out.println("m1()");
}
public static void main(String[] args) {
Person p1 = new Person("张三");
p1.m1();
Person.m1();
int arr[] = {111, 223, 444};
System.out.println(Arrays.toString(arr));
}
}
静态方法调用的注意事项:
- 静态方法中不能出现this关键字
- 静态方法中只能直接访问静态成员变量和静态成员方法
- 静态方法中不能直接访问非静态成员变量和非静态成员方法
- 非静态方法中可以直接访问一切成员变量和成员方法
public class Person {
String name;
static int id;
public Person(String name) {
this.name = name;
++id;
}
public void m2(){
this.name = "haha";
id = 100;
}
public static void m1(){
// this.name 错
// name = "" 错
id = 100;
}
以后开发中static的应用
概述
以后的项目中,通常会需要一些“全局变量”或者“全局的工具方法”,这些全局变量和方法,可以单独定义在一个类中,并声明为static(静态)的,可以很方便的通过类名访问
package com.company.day06.utils;
public class Utils {
// 静态代码块, 优先于main方法执行
static {
AGE = 10;
}
public static int AGE;
// "全局变量"
public static final int WIDTH = 800;
public static final int HEIGHT = 800;
// "全局方法"
// 找int数组中的最大值
public static int getArrayMax(int[] arr){
int max = arr[0];
for (int i = 0; i < arr.length; i++) {
if(arr[i] > max){
max = arr[i];
}
}
return max;
}
}
import com.company.day06.utils.Utils;
public class Test4 {
public static void main(String[] args) {
System.out.println(Utils.WIDTH);
System.out.println(Utils.HEIGHT);
int[] arr = {122, 334,444};
System.out.println(Utils.getArrayMax(arr));
System.out.println(Utils.AGE);
}
}
接口
- 引用数据类型除了类其实还有接口,接下来学习接口的概述
概述: 接口是Java语言中的一种引用类型,是方法的"集合",所以接口的内部主要就是定义方法,包含常量,抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(jdk9)。
接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。
public class 类名{}–>.class
public interface 接口名{}->.class
引用数据类型:数组,类,接口
接口的使用,它不能创建对象,但是可以被实现(implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类
格式
public interface 接口名称 {
// 常量(jdk7及其以前)
// 抽象方法(jdk7及其以前)
// 默认方法(jdk8)
// 静态方法(jdk8)
// 私有方法(jdk9)
}
package com.company.day06.jiekou;
public interface IA {
// 常量(jdk7及其以前) 使用public static final关键字修饰,这三个关键字都可以省略
public static final int NUM = 10;
// 抽象方法(jdk7及其以前) 使用public abstract关键字修饰,这2个关键字都可以省略
public abstract void method1();
void method2();
// 默认方法(jdk8) 使用public default关键字修饰,public可以省略,default不可以省略
public default void method3(){
System.out.println("默认方法");
}
// 静态方法(jdk8) 使用public static关键字修饰,public可以省略,static不可以省略
public static void method4(){
System.out.println("静态方法");
}
// 私有方法(jdk9) 使用private关键字修饰,private不可以省略
private void method5(){
System.out.println("method5");
}
private static void method6(){
System.out.println("method6");
}
}
实现接口
类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。
public class ZI implements IA, IB{
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void show() {
}
}
接口中成员的访问特点:
- 接口中的常量: 主要是供接口直接使用
- 接口中的抽象方法: 供实现类重写的
- 接口中的默认方法: 供实现类继承的(实现类中可以直接调用,实现类对象也可以直接调用)
- 接口中的静态方法: 只供接口直接调用,实现类继承不了
- 接口中的私有方法: 只能在接口中直接调用,实现类继承不了
多实现时的几种冲突情况
- 公有静态常量的冲突
- 公有抽象方法的冲突
- 公有默认方法的冲突
- 公有静态方法的冲突
- 私有方法的冲突
公有静态常量的冲突
public interface A {
public static final int NUM1 = 10;
}
public interface B {
public static final int NUM1 = 20;
public static final int NUM2 = 30;
}
public class Impl implements A, B{
public static void main(String[] args) {
// 公有静态常量的冲突: 如果多个接口中有相同的常量,那么实现类就无法继承
// System.out.println(Impl.NUM1); // 报错
System.out.println(Impl.NUM2);
}
}
公有抽象方法的冲突
实现类只需要重写一个
public interface A {
public static final int NUM1 = 10;
public abstract void method();
}
public interface B {
public static final int NUM1 = 20;
public static final int NUM2 = 30;
public abstract void method();
}
public class Impl implements A, B{
public static void main(String[] args) {
// 公有静态常量的冲突: 如果多个接口中有相同的常量,那么实现类就无法继承
// System.out.println(Impl.NUM1); // 报错
System.out.println(Impl.NUM2);
}
@Override
public void method() {
System.out.println("重写");
}
}
公有默认方法的冲突
- 实现类必须重写一次最终版本
public interface A {
public static final int NUM1 = 10;
public abstract void method();
public default void method2(){
System.out.println("A 接口的默认方法method2");
}
}
public interface B {
public static final int NUM1 = 20;
public static final int NUM2 = 30;
public abstract void method();
public default void method2(){
System.out.println("B 接口的默认方法method2");
}
}
public class Impl implements A, B{
@Override
public void method() {
System.out.println("重写");
}
@Override
public void method2() {
B.super.method2();
}
public static void main(String[] args) {
Impl impl = new Impl();
impl.method2();
}
}
公有静态方法的冲突:
静态方法是直接属于接口的,不能被继承,所以不存在冲突
私有方法的冲突
- 私有方法只能在本接口中直接使用,不存在冲突
抽象类和接口的练习
通过实例进行分析和代码演示抽象类和接口的用法。
1、举例:
犬: —抽象父类
行为:吼叫;吃饭;
缉毒犬:继承犬类,实现缉毒接口
行为:吼叫;吃饭;缉毒;
缉毒接口:
缉毒
- 如果一个父类中的某个方法,所有子类都有不同的实现,那么该方法就应该定义成抽象方法,所以该父类就是抽象类 (父类一般都是抽象类)
- 如果某个功能是一个类额外增加的,那么就可以把这个额外的功能定义到接口中,再这个类去实现
分析:
由于犬分为很多种类,他们吼叫和吃饭的方式不一样,在描述的时候不能具体化,也就是吼叫和吃饭的行为不能明确。当描述行为时,行为的具体动作不能明确,这时,可以将这个行为写为抽象行为,那么这个类也就是抽象类。
可是有的犬还有其他额外功能,而这个功能并不在这个事物的体系中 , 例如 : 缉毒犬。缉毒的这个功能有好多种动物都有 , 例如 : 缉毒猪 , 缉毒鼠。我们可以将这个额外功能定义接口中 ,让缉毒犬继承犬且实现缉毒接口 , 这样缉毒犬既具备犬科自身特点也有缉毒功能。
- 额外的功能—> 在接口中定义,让实现类实现
- 共性的功能—> 在父类中定义,让子类继承
public interface JiDu {
public abstract void jiDu();
}
public abstract class Dog {
public abstract void houJiao();
public abstract void eat();
}
public class JiDuDog extends Dog implements JiDu{
@Override
public void houJiao() {
System.out.println("缉毒犬找到了毒品,开始吼叫,汪汪汪....");
}
@Override
public void eat() {
System.out.println("缉毒之前,开始吃骨头...");
}
@Override
public void jiDu() {
System.out.println("吃完东西后,开始使用鼻子查找毒品....");
}
}
public class Test {
public static void main(String[] args) {
JiDuDog jiDuDog = new JiDuDog();
jiDuDog.eat();
jiDuDog.jiDu();
jiDuDog.houJiao();
}
}