Java基础重构-面向对象(下)

简介: Java 是面向对象的程序设计语言,类是面向对象的重要内容,可以把了当成一种自定义类型。可以使用类来定义变量,这种类型的变量统称为引用变量。

抽象类的作用是什么?

抽象类不能创建实例,只能当成父类来继承。从语义的角度来说,抽象类是从多个具体类中抽象出来的父类,他具有更高层此的抽象。从多个具有相同特征的类中抽象出了一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。


抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展,改造,但子类总体上会大致保留抽象类的行为方式。

什么是接口?

在java8里,允许为接口定义默认方法,类方法。


接口从宏观上来说,是从多个相似类中抽象出来的规范,接口不提供任何实现。说简单点,接口反映的是一类事物的方法。


接口里可以包含成员变量(只能是静态常量),方法(只能是抽象实例方法,类方法或默认方法),内部类(内部接口,枚举)


接口支持多继承。

谈谈你对接口和抽象类的理解?

接口和抽象类具有如下相同特征:

  • 接口和抽象类都不能被实例化,他们都位于继承树的顶端,用于被其他类实现和继承。
  • 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。
  • 但接口和抽象类之间的差别非常大,这种差别主要体现在二者的设计目的上。
  • 接口体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务(以方法的形式提供);对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务(就是如何来调用方法)。当一个程序中使用接口时,接口时多个模块间的耦合标准。从某种程度上来看,接口类似于整个系统的 总纲 ,它制定了系统各模块应该遵循的标准,因此一个系统中的接口不应该经常改变。一旦接口被改变,对整个系统升值其他系统的影响将是辐射性的,导致系统中大部分类都需要改写。


抽象类作为系统中多个子类的共同父类,它所体现的是一种模板设计。抽象类作为多个子类的抽象父类,可以被当成系统实现过程中的中间产品,这个中间产品已经实现了系统的部分功能(那些已经提供实现的方法),但这个产品依然不能被当成最终产品,必须有跟进异步的完善,这汇总完善可能有几种不同的方式。


在用法上,抽象类和接口也存在如下差别:

  1. 接口里只能包含抽象方法和默认方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。
  2. 接口里不能定义静态方法,抽象类里可以定义静态方法。
  3. 接口里只能定义静态常量,不能定义普通成员变量;抽象类既可以定义普通成员变量,也可以定义静态常量。
  4. 接口i不包含构造器,抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
  5. 接口里不能包含初始化块,但抽象类则完全可以包含初始化块。
  6. 接口可以多实现,抽象类只能单继承。

什么是内部类?

将一个类放在另一个类的内部定义,这个定义在其他类内部的类被称为内部类,包含内部类的类也被称为外部类。

内部类的作用如下:

  • 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
  • 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。
  • 匿名内部类适合用于创建那些仅需要一次使用的类。


内部类与外部类还与如下区别:

  • 内部类比外部类多使用三个修饰符,pivate ,protected,static 外部类不可以使用这三个修饰符。
  • 非静态内部类不能拥有静态成员。

为什么静态内部的实例方法也不能访问外部类的实例属性?

因为静态内部类是外部类的类相关的,而不是外部类的对象相关的。也就说说,静态内部类对象不是寄生在外部类的实例中,而是寄生在外部类的本身中。当静态内部类对象存在时,并不存在一个被它寄生的外部类对象,静态内部类对象只持有外部类的类引用,没有持有外部类对象访问。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到被寄生的外部类对象,这将引起错误。

Lambda表达式的使用

  • 形参列表.形参列表允许省略形参列表。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
  • 箭头 (->)
  • 代码块。如果代码块只包含一条语句,lambda 表达式允许省略代码块的花括号。如果Lambda 代码只有一条return语句,那么省略return 关键字。

遍历list 的方式

  List<String> list=new ArrayList<>();
        list.add("123");
        list.add("123");
        list.add("123");
        list.add("123");
        list.add("123");
        list.forEach(System.out::println);

实现Runnable接口

  new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(123);
            }
        }).start();
        new Thread(()-> System.out.println("123")).start();

使用匿名内部类

Runnable runnable=new Runnable() {
    @Override
    public void run() {
        System.out.println(123);      
    }
};
Runnable r1=()-> System.out.println("123");
r1.run();

如下写法

public class MyClass {
    public static void main(String[] args) {
            new MyClass().eat(()-> System.out.println("132"));
            new MyClass().fly(weather -> {
                System.out.println("今天");
            });
            new MyClass().test((a,b)->a+b);
    }
    public void eat(Eatable e){
        System.out.println(e);
        e.taste();
    }
    public void fly(Flyable f){
        f.fly("Petterp");
    }
    public void test(Addable a){
        a.add(5,3);
    }
}
interface Eatable{
    void taste();
}
interface Flyable{
    void fly(String weather);
}
interface Addable{
    int add(int a,int b);
}

什么是枚举?

枚举类是一种特殊的类,他一样可以有自己的成员变量,方法,可以实现一个或者多个接口,也可以定义自己的构造器。一个 Java源文件中最多只能定义一个 public 访问权限的枚举类,且该 Java 源文件也必须和枚举类的类名相同。

枚举类与普通类之间有如下区别:

  • 枚举类可以实现一个或多个接口,使用 enum 定义的枚举类默认集成了 java.lang.Enum 类,而不是默认集成 Object 类,因此枚举类不能显示继承其他父类。其中java.lang.Enum类实现了 JAVA.langSerializable 和 java.lang.Comparable两个接口。
  • 使用Enum 定义,非抽象的枚举类默认会使用 final 修饰,因此枚举类不能派生子类。
  • 枚举类的构造器只能使用 private 访问控制符,如果省略了构造器的访问控制符,则默认使用 private修饰,如果强制指定访问控制符,则 只能 指定 private修饰符。
  • 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加 public static fianl 修饰。
枚举类的使用:
public enum SeasonEnum {
    a,b,c,d;
    public String name;
}
class Demo{
    public static void main(String[] args) {
        for (SeasonEnum s:SeasonEnum.values()){
            //打印枚举值的索引
            System.out.println(s.ordinal());
        }
        //如果该枚举对象位于指定枚举对象之后,则返回正整数;
        //如果该枚举对象位于指定枚举对象之前,则返回负整数,否则返回0
        System.out.println(SeasonEnum.valueOf("a").compareTo(SeasonEnum.valueOf("b")));
        //创建枚举类对象
        SeasonEnum seasonEnum=Enum.valueOf(SeasonEnum.class,"a");
        seasonEnum.name="Petterp";
        System.out.println(seasonEnum.name);
    }
}

枚举类也可以用于 switch


枚举类带构造器的使用

public enum SeasonEnum {
    //此处的枚举值必须调用对应的构造器来创建
    MALE("男"), FEmale("女");
    private final String name;
    //枚举类的构造器只能使用private修饰
     SeasonEnum(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
class Demo {
    public static void main(String[] args) {
        System.out.println(SeasonEnum.FEmale);
    }
}

以上代码如同于以下代码

public  static  final SeasonEnum MALE=new SeasonEnum("男");
public  static  final SeasonEnum FEmale=new SeasonEnum("女");

在枚举类中列出枚举值是,实际上就是调用构造器创建枚举类对象,只是这里无须使用new 关键字,也无需显示调用构造器。前面列出枚举值是无须传入参数,甚至无须使用括号,仅仅是因为前面的枚举类包含无参数的构造器。

实现接口的枚举类

枚举类也可以实现一个或多个接口,与普通类实现一个或多个接口完全一样,枚举类实现一个或多个接口时,也需要实现该接口所包含的方法。

public enum SeasonEnum implements Port {
    //此处的枚举值必须调用对应的构造器来创建
    MALE("男") {
        @Override
        public void info() {
            System.out.println("这个枚举值代表男性");
        }
    },
    FEmale("女") {
        @Override
        public void info() {
            System.out.println("这个枚举值代表女性");
        }
    };
    private final String name;
    //枚举类的构造器只能使用private修饰
    SeasonEnum(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
class Demo {
    public static void main(String[] args) {
       SeasonEnum.FEmale.info();
    }
}

上面的代码看起来有些奇怪,当创建 MALE 和 FEMALE 两个枚举值时,后面又紧跟了一对花括号,这队花括号包含了一个 info 方法定义。花括号部分实际上就是一个类体部分,在这种情况下,当创建 MALE,FEMALE 枚举值是,并不是直接创建 SeasonEnum枚举类的实例,而是相当于创建 SeasonEnum的匿名子类的实例。因为粗体字括号部分实际上是匿名内部类的类体部分,所以这个部分的代码语法与匿名内部类语法大致相似,只是它依然是枚举类的匿名内部类。

枚举类不是用 final 修饰吗,为什么还可以派生子类?

并不是所有的枚举类都是用了 final 修饰,非抽象的枚举类才默认使用 final 修饰,对于一个抽象的枚举类而言,只要它包含了抽象方法,它就是抽象枚举类,系统会默认是使用 abstart修饰。

枚举类可以包含抽象方法吗?
public enum Operation {
    Plus {
        @Override
        public double eval(double x, double y) {
            return 0;
        }
    },
    MINUS{
      public double eval(double x,double y){
          return x-y;
      }
    };
    //为枚举类定义一个抽象方法
    //这个抽象方法由不同的枚举值提供不同的实现
    public abstract  double eval(double x,double y);
    public static void main(String[] args) {
        System.out.println(Operation.Plus.eval(3,4));
    }
    //枚举类里定义抽象方法是不能使用 abstart 关键字将枚举类定义成抽象类(因为系统自动会为它添加 abstart 关键字),但因为枚举类需要显示创建枚举值而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现,否则将出现编译错误。
目录
相关文章
|
4天前
|
Java
Java编程思想(面向对象)第四版PDF分享
探索《Java编程思想》第四版,理解Java不仅是特性集合,更是解决问题的强大工具。本书深入设计层面,构建编程思维模型,助你逐步精通Java。[阅读更多](https://zhangfeidezhu.com/?p=355) ![Java编程思想](https://ucc.alicdn.com/pic/developer-ecology/nrw3f3oqlpmag_c8ff959a921545f1bbabcefd37f029cf.png)
19 1
Java编程思想(面向对象)第四版PDF分享
|
4天前
|
安全 Java
|
9天前
|
存储 Java C语言
Java面向对象课程设计--类管理系统
Java面向对象课程设计--类管理系统
15 1
|
9天前
|
Java
Java面向对象特征(二)----- 继承
Java面向对象特征(二)----- 继承
Java面向对象特征(二)----- 继承
|
2天前
|
存储 安全 Java
Java基础系列1:Java面向对象
Java基础系列1:Java面向对象
|
2天前
|
Java 数据安全/隐私保护
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
9 0
|
2天前
|
存储 Java
Java基础手册(标识符 关键字 字面值 变量 数据类型 字符编码 运算符 控制语句 方法及方法重载和递归 面向对象与面向过程)
Java基础手册(标识符 关键字 字面值 变量 数据类型 字符编码 运算符 控制语句 方法及方法重载和递归 面向对象与面向过程)
7 0
|
2天前
|
Java 关系型数据库
Java中的面向对象设计原则与实践
Java中的面向对象设计原则与实践
|
2天前
|
设计模式 Java 开发者
Java中的代码优雅重构实战
Java中的代码优雅重构实战
|
9天前
|
Java
Java面向对象特征(一)----- 封装
Java面向对象特征(一)----- 封装