在Java 中,当一个类被 abstract 关键字修饰的时候这个类称为抽象类。当一个类的方法被 abstract 关键字修饰的时候,该方法称为抽象
方法。抽象方法必须定义在抽象类中。当一个方法被定义为抽象方法后,意味着该方法不会有具体的实现,而是在抽象类的子类中通过方法重写进行实现。定义抽象方法的语法格式如下:
[访问修饰符]abstract <返回类型><方法名>([参数列表]);
abstract 关键字表示该方法被定义为抽象方法。
普通方法和抽象方法相比,主要有下列两点区别:
在Java中,当一个类被abstract关键字修饰时,该类为抽象类。定义抽象类的方法如下:
abstract class <类名>{
}
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 返回值类型 方法名称(参数列表);
修饰符 返回值类型 方法名称(参数列表){
}
}
提示:
抽象类和具体类是面向对象编程中的两个重要概念
**抽象类: **抽象类是一种不能被实例化的类,它只能被用作其他类的基类。它的目的是为了提供一种基于继承的模板,强制要求子类实现它的抽象方法。抽象类中可能包含一些实现方法,但是也可以只包含抽象方法。
抽象类的主要特点:
具体类: 具体类是可以被实例化的类,它可以直接使用,也可以继承自其他类。具体类中必须要实现它的所有方法,不能有未实现的方法。它可以包含属性、方法、构造函数等。
具体类的主要特点:
抽象类和具体类在定义上的差异:抽象类是一种不能被实例化的类,它只能被用作其他类的基类。具体类则是可以被实例化的类。
抽象类和具体类在实现上的差异:抽象类可以包含抽象方法和非抽象方法,子类必须实现抽象方法; 具体类必须实现它的所有方法,不能有未实现的方法。
抽象类和具体类在使用上的差异:抽象类通常用于定义一些基础的、通用的行为和属性,而具体类则用于实现具体的业务逻辑和行为。
总的来说,抽象类和具体类是两种不同类型的类,它们的作用和使用场景也不同。
抽象类用于定义一些通用的行为和属性,并强制子类实现抽象方法,具体类则用于实现具体的业务逻辑和行为。
内部类是指讲一个类定义在另外一个类的内部,称为内部类。
成员类可以解决类单继承问题
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();
}
}
在一个类中声明的类称为内部类. 内部类是一个嵌套类.
内部类包含以下几种:
之前定义的类都是命名类,也就是类都有明确的名称。匿名类是指没有明确名称的类。
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() {
@Override
public void play() {
System.out.println("发发发");
}
};
stu.play();
}
}
接口中有一种特殊的接口,这类接口中只有一个抽象方法。我们把这种接口称为函数式接口(FunctionalInterface) 可以使用 @FunctionalInterface 标记函数接口 函数接口表示某个功能(能力)
函数式接口的匿名类写法可以使用 lambda
来简写.
// 只能实现共有的匿名类
@FunctionalInterface
interface IType {
void test();
}
class Demo{
public static void main(String[] args){
// 通常使用 匿名类
IType a = new IType(){
@Override
public void test(){
// 实现
}
};
// lambda
IType b = () ->{
// 实现
};
}
}
如果lambda
中实现只有一句代码则可以省略 {}
这种写法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 接口名{
//实现方法
// 普通方法
// 属性
}
解析:
java 中继承是单继承,使用 extends 关键字;
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接口");
}
}
public class LenovoComputer extends Computer implements USBInterface, ChargeInterface{
}
public interface USBC extends USBInterface,ChargeInterface{
}
**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
@Override
public void a() {
System.out.println("Usbfeng");
}
@Override
public void b(){
System.out.println("默认default覆盖写法");//重写默认b()方法完全可以,最后实例化对象调用
}
}
public class Usbshan implements Usb{//实现类二
@Override
public 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();//调用mo
Usb.c();
}
}
我们需要抽取一个共有方法,用来解决多个默认方法之间重复代码的问题,但是这个共有方法不应该让实现类使用,所以私有化
解决方案:
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 返回值类型 方法名称 (参数列表){
方法体
}
特性 | 接口 | 抽象类 |
---|---|---|
组合 | 可以在新类中组合多个接口 | 只能继承一个抽象类 |
状态 | 只含有静态字段、抽象方法、默认方法、静态方法 | 可以包含字段 |
默认方法和抽象方法 | 默认方法不需要在子类里实现,它只能引用接口中的方法 | 抽象方法必须在子类里实现 |
构造器 | 不能有构造器(接口没有构造、不能实例化) | 有构造、同样不能实例化 |
访问权限 | 隐式 public | 可以为 protected 或包访问权限 |
举例:
@FunctionalInterface
interface IType {
void test();
}
class Demo{
public static void main(String[] args){
// 通常使用 匿名类
IType a = new IType(){
@Override
public void test(){
// 实现
}
};
// lambda
IType 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;
}