Java基础11——抽象类和接口
抽象类和抽象方法
区分普通方法和抽象方法
在Java 中,当一个类被 abstract 关键字修饰的时候这个类称为抽象类。当一个类的方法被 abstract 关键字修饰的时候,该方法称为抽象
方法。抽象方法必须定义在抽象类中。当一个方法被定义为抽象方法后,意味着该方法不会有具体的实现,而是在抽象类的子类中通过方法重写进行实现。定义抽象方法的语法格式如下:
[访问修饰符]abstract <返回类型><方法名>([参数列表]);
abstract 关键字表示该方法被定义为抽象方法。
普通方法和抽象方法相比,主要有下列两点区别:
- 抽象方法需要用修饰符 abstract 修饰,普通方法不允许
- 普通方法有方法体,抽象方法没有方法体。
区分普通类和抽象类
在Java中,当一个类被abstract关键字修饰时,该类为抽象类。定义抽象类的方法如下:
abstract class <类名>{}
abstract关键字表示该类被定义为抽象类。
普通类与抽象类相比,主要有下列两点区别:
- 抽象类需要用修饰符 abstract 修饰,普通类不允许
- 普通类可以被实例化,抽象类不可以被实例化。
public abstract class Person {//属性private String name = "无名氏";private int age = 100;private int weight = 0;//构造方法public Person(String name){this.name = name;}public void print(){System.out.println("我的名字是"+ this.name + ",年龄是" + this.age + ",体重是"+this.weight ".");}
}class Test{public static void main(String[] args) {//Pet pet = new Pet("大黄");//错误示范,因为抽象类不能实例化}
}
定义一个抽象类
当一个类被定义为抽象类时,它可以包含各种类型的成员,包括属性、方法等,其中方法又分为普通方法和抽象方法,下面是抽象类结构的示例:
public abstract class 类名{修饰符 数据类型 变量名;修饰符 abstract 返回值类型 方法名称(参数列表);修饰符 返回值类型 方法名称(参数列表){}
}
提示:
- abstract 修饰的抽象方法没有方法体
- private 关键字不能用来修饰抽象方法,否则由于封装导致子类无法重写抽象方法
- 抽象类不能被之间实例化,但有构造方法
- 子类如果不是抽象类,则子类必须重写抽象类中的全部抽象方法
- abstract 修饰符不能和final修饰符一起使用
抽象类和具体类的比较
抽象类和具体类是面向对象编程中的两个重要概念
-
**抽象类: **抽象类是一种不能被实例化的类,它只能被用作其他类的基类。它的目的是为了提供一种基于继承的模板,强制要求子类实现它的抽象方法。抽象类中可能包含一些实现方法,但是也可以只包含抽象方法。
-
抽象类的主要特点:
- 不能被实例化
- 可以包含抽象方法和非抽象方法
- 子类必须实现抽象方法
- 抽象类可以包含构造函数。
-
具体类: 具体类是可以被实例化的类,它可以直接使用,也可以继承自其他类。具体类中必须要实现它的所有方法,不能有未实现的方法。它可以包含属性、方法、构造函数等。
-
具体类的主要特点:
- 可以被实例化
- 必须实现所有方法,可以包含属性、方法、构造函数等。
两者的比较:
-
抽象类和具体类在定义上的差异:抽象类是一种不能被实例化的类,它只能被用作其他类的基类。具体类则是可以被实例化的类。
-
抽象类和具体类在实现上的差异:抽象类可以包含抽象方法和非抽象方法,子类必须实现抽象方法; 具体类必须实现它的所有方法,不能有未实现的方法。
-
抽象类和具体类在使用上的差异:抽象类通常用于定义一些基础的、通用的行为和属性,而具体类则用于实现具体的业务逻辑和行为。
总的来说,抽象类和具体类是两种不同类型的类,它们的作用和使用场景也不同。
抽象类用于定义一些通用的行为和属性,并强制子类实现抽象方法,具体类则用于实现具体的业务逻辑和行为。
内部类
内部类是指讲一个类定义在另外一个类的内部,称为内部类。
成员类可以解决类单继承问题
public class AbstractDemo {//外部类private static int a =0;private int b =3;public void a(){
// Abstr abstr = new Abstr();new Abstr().test();Abstr.abc();}static class Abstr {private int aa =3;private static int bb=2;public void test(){int a = 5;System.out.println(aa);aa=2;System.out.println(aa);System.out.println(bb);Abstr.bb=4;System.out.println(bb);System.out.println(AbstractDemo.a);AbstractDemo.a=5;System.out.println(AbstractDemo.a);System.out.println(a);}public static void abc(){System.out.println("a");Abstr.bb=3;new Abstr().aa=1;}}
class Abstra{//内部类private static int b =3;private int aa =2;public void test1(){int b =2;System.out.println(b);//当前类的对象AbstractDemo.this.b=5;//外部类的对象System.out.println(AbstractDemo.this.b);}}public static void main(String[] args) {AbstractDemo.Abstr ab = new AbstractDemo.Abstr();ab.test();Abstr.abc();AbstractDemo.Abstra abs = new Abstractemo().new Abstra(); // abs.test1();}
}
内部类
在一个类中声明的类称为内部类. 内部类是一个嵌套类.
内部类包含以下几种:
- 成员类。在类的里面直接声明的类
- 局部类。声明在一个块中, 当 main 方法调用结束,就销毁
- 匿名类。
匿名类
之前定义的类都是命名类,也就是类都有明确的名称。匿名类是指没有明确名称的类。
public abstract class AbstractDemo2 {public static void test(){System.out.println("哈哈哈");}public abstract void play();
}public TestAbstract() {}
public static void main(String[] args) {AbstractDemo2 stu = new AbstractDemo2() {@Overridepublic void play() {System.out.println("发发发");}};stu.play();
}
}
Notes:
- static 成员类, 使用 类名. 调用. 使用 new 外部类.内部类();
- 实例成员类, 使用外部类的对象来调用. 使用 对象.new 内部类();
- 成员类属于类的成员,所以可以调用外部类的 private 成员
- 在实例成员类中可以使用 外部类.this 表示外部类对象 (实例成员类是由外部类对象创建的)
- 成员类中的 this 表示的是成员类
- 外部类可以直接调用成员类的静态成员,实例成员需要创建成员类的对象来调用
- 外部类可以访问成员类的 private 成员
- 类的成员: 字段、方法、成员类/接口
- 成员类(内部类): 在类的里面声明的类. 可以被继承
- 外部类的访问修饰符只能是: public / package-access
- 成员类的访问修饰符可以是所有的访问修饰符
- 可以使用 final static abstract 等修饰符修饰成员类
接口基本概念
约定好规范,然后按照规范来做。接口就是定义规范。
- 描述规范的数据类型
- 只定义规范(不需要实现):提供一种约定,使实现接口的类在形式上保持一致。
- Java中规范可以用接口
interface
来表示
抽象类中可以有普通方法,而接口中的方法默认都是抽象的,也可以说接口是一个 ”特殊的 抽象类 “,接口不能被实例化,而且没有构造方法。
接口的格式
public interface 接口名{// 接口成员
}
解析:
-
定义接口使用interface修饰符
-
一个接口可以继承其它接口,称为父接口,且接口可以多继承;它会继承父接口中声明的常量和抽象方法
-
成员列表中的成员变量声明[public][static][final] 数据类型 成员变量名 = 常量;即,接口中的成员变量默认都是public、static、final的,因此,public、static、final 可以省略
-
成员列表中的成员方法声明[public][abstract] 返回值类型 方法名称(参数列表);即接口中的方法默认都是public、abstract的,因此,public、abstract可以省略
-
接口中的变量只能是静态常量( static final ) , 所以可以省略 public static final ,静态常量在定义时就要赋值,且不可变。
接口的使用
与抽象类一样,使用接口要通过子类,子类通过implements关键字实现接口。
子类通过 implements 关键字实现接口,实现接口就必须实现(重写)接口中的抽象方法
实现接口的语法格式如下:
[修饰符] 类名 implements 接口名{ //实现方法// 普通方法// 属性}
解析:
- 实现接口要用implements关键字
- 一个类可以实现多个接口,各接口之间用逗号分隔
- 实现接口的类必须实现接口中定义的所有抽象方法,即使类中不使用某个抽象方法也必须实现它,通常用空方法体实现子类不需要的抽象方法,如果抽象方法有返回值,可返回默认值。
- 接口的实现类允许包含普通方法
- 在实现抽象方法时需要指定public权限,否则会产生编译错误
- 类和类之间叫做继承,用extends
- 类和接口之间叫做实现implement;
多个接口实现
java 中继承是单继承,使用 extends 关键字;
一个接口实现类可以实现多个接口,使用 implements ,多个接口之间用 , 隔开。
public class Computer implements USBInterface, ChargeInterface{ //定义一个实现类实现多个接口public void play() { // 普通方法System.out.println("play game"); }@Override
public void charge() { //重写抽象方法System.out.println("充电");
}@Override
public void service() { //重写抽象方法System.out.println("USB接口"); }
}
一个类可以同时继承和实现接口, extends 要在 implements 之前
public class LenovoComputer extends Computer implements USBInterface, ChargeInterface{}
接口与接口之间是继承关系,使用 extends 关键字。多个接口使用 , 隔开
public interface USBC extends USBInterface,ChargeInterface{ }
JDK8.0 接口新特性 default 和 static
**default:**在 jdk8.0 中 default 关键字可用于在接口中修饰方法(默认方法), default 修饰的方法可以有具体实现,也只能在接口中出现。 default 修饰的方法可以被重写。
默认方法可以在不破坏已经在使用该接口的所有代码。默认方法有时也称为防御方法(defender method)或 虚拟扩展方法(virtual extension method)
**static:**接口中还可以有 static 修饰的方法,称为静态方法(类方法)。 static 方法必须直接使用 接口名.方法名调用。
从Java 8 开始,接口允许定义默认方法(注意是在接口), 格式:
public default 返回值 方法名(){方法体}
从Java 8 开始,接口允许定义静态方法。
public static 返回值 方法名(){方法体}
接口名称。静态方法名(参数列表);
public interface Usb {//接口//接口中的抽象方法public abstract void a();//新添加的默认方法public default void b(){System.out.println("实现接口升级");}//新添加的静态方法public static void c(){System.out.println("实现静态方法");}
}
public class Usbfeng implements Usb{//实现类1@Overridepublic void a() {System.out.println("Usbfeng");}
@Override
public void b(){System.out.println("默认default覆盖写法");//重写默认b()方法完全可以,最后实例化对象调用
} }
public class Usbshan implements Usb{//实现类二@Overridepublic void a() {System.out.println("Usbshan");}
}
1.接口的默认方法,可以通过接口实现类对象,直接调用
2.接口的默认方法,也可以被接口实现类进行覆盖重写
public class Test {//测试类public static void main(String[] args) {
//创建了实现类对象Usbfeng usbfeng = new Usbfeng();usbfeng.a();//调用抽象方法,实现的是右侧实现类usbfeng.b();//调用默认方法,如果实现类中没有,会向上找接口Usbshan usbshan = new Usbshan();usbshan.a();usbshan.b();//调用moUsb.c();}
}
JDK9 接口新特性 private
我们需要抽取一个共有方法,用来解决多个默认方法之间重复代码的问题,但是这个共有方法不应该让实现类使用,所以私有化
解决方案:
从Java 9开始接口当中允许定义私有方法。
1.普通私有方法,解决多个默认方法之间重复代码的问题。
private 返回值类型 方法名称(参数列表){方法体
}
public interface Typc {//代码重复率高public default void a(){System.out.println("默认方法A");System.out.println("AAA");System.out.println("BBB");System.out.println("CCC");}
public default void b(){System.out.println("默认方法B");System.out.println("AAA");System.out.println("BBB");System.out.println("CCC");
}
}
运用私有化后public interface Typc {//因为被私有化,只有a和b能够使用c,其余的地方无法使用public default void a(){System.out.println("默认方法A");c();}public default void b(){System.out.println("默认方法B");c();}private void c(){System.out.println("AAA");System.out.println("BBB");System.out.println("CCC");}
}
测试public class Testtypec {public static void main(String[] args) {Typcc typcc = new Typcc();typcc.b();typcc.a();}
}输出结果
静态私有方法,解决多个静态方法之间重复代码问题
private static 返回值类型 方法名称 (参数列表){方法体
}
- 静态私有方法同理,只有本接口多个静态方法可以使用,其余作用域无法使用。
- 在Java9以后出现并使用
- JDK9 接口中可以使用 private 修饰方法,供接口中其他方法调用。
抽象类和接口的区别(重点)
特性 | 接口 | 抽象类 |
---|---|---|
组合 | 可以在新类中组合多个接口 | 只能继承一个抽象类 |
状态 | 只含有静态字段、抽象方法、默认方法、静态方法 | 可以包含字段 |
默认方法和抽象方法 | 默认方法不需要在子类里实现,它只能引用接口中的方法 | 抽象方法必须在子类里实现 |
构造器 | 不能有构造器(接口没有构造、不能实例化) | 有构造、同样不能实例化 |
访问权限 | 隐式 public | 可以为 protected 或包访问权限 |
举例:
@FunctionalInterface
interface IType {void test();
}
class Demo{public static void main(String[] args){// 通常使用 匿名类IType a = new IType(){@Overridepublic void test(){ // 实现}};// lambdaIType b = () ->{// 实现};
如果lambda
中实现只有一句代码则可以省略{}
这种写法,java
可以根据变量的类型推断出匿名类实现的接口,以及重写的方法。方法的写法上:
-
如果方法没有参数则使用
()
表示。 -
如果有参数,则需要在
()
写形参列表,在实现中可以使用这些参数。 -
参数的类型可以省略,可以推断出参数类型
-
如果方法没有参数则使用
()
表示。 -
如果只有一个参数
()
可以省略。
深克隆和浅克隆
浅克隆
浅克隆指创建一个新对象,并将原始对象的所有非静态字段的值复制到新对象中。如果字段的类型是基本类型,那么会复制其值;如果字段的类型是引用类型,则会复制引用,即新对象和原始对象将引用同一个对象。
深克隆
深克隆是指创建一个新对象,并将原始对象的所有字段新对象中,包括引用类型字段所引用的对象。
简单来说就是浅克隆对象中的引用数据类型字段是同一个引用,深克隆会比浅克隆多克隆一个引用数据类型
protected Person clone() throws CloneNotSupportedException {// return (Person) super.clone();Person copy = (Person) super.clone();// 将引用数据类型 clone 一份, 再设置Dog copyDog = copy.getDog().clone();copy.dog = copyDog;return copy;
}