Part1 面向对象(下)

一、抽象类和抽象方法

1. 抽象方法

使用abstract修饰的方法,没有方法体,只有方法声明,定义的是一种“规范”,要求子类提供具体的实现。

2.抽象类

包含抽象方法的类就是抽象类,同样需要用abstract修饰。有抽象方法的类一定要定义为抽象类,但抽象类可以没有抽象方法,抽象类内也可以包含具体实现的方法。抽象类是从多个具体类中抽象出来的父类,它具有更高层次的抽象,以这个类作为模板,可以避免子类设计的随意性。

3.抽象方法和抽象类的要点

  • 抽象类不能实例化,即不能通过new创建对象,只能被继承。
  • 抽象类可以包含方法(普通方法和抽象方法)、成员变量、构造方法、初始化块、内部类(接口、枚举),但构造方法不能用来new对象,只能用来被子类调用。
  • 抽象类只能被继承。
  • 抽象方法必须被子类实现。
  • 含有抽象方法的类(直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法)只能被定义为抽象类。
  • 使用abstract修饰类时,表明这个类只能被继承,修饰方法时,表明这个方法必须由子类提供实现。但final修饰的类不能被继承,final修饰的方法不能被重写,因此abstract和final不能同时使用。
  • abstract只能修饰类或者方法,不能修饰变量、构造方法等。
  • 当使用static修饰方法时,可以通过类调用这个方法,但如果这个方法又被abstract修饰,调用一个没有方法体的方法会出现错误,所以static和abstract不能修饰同一个方法。但它们不是绝对互斥的,它们可以同时修饰内部类。
  • abstract修饰的方法必须被子类重写才有意义,因此abstract方法不能被private修饰,即private和abstract不能同时修饰方法。

    1. //抽象类
    2. abstract class Animal{
    3. private int age;
    4. abstract public void shout();//抽象方法,具体的子类实现方法不一样,这里只提供这个方法的抽象
    5. public void run(){
    6. System.out.println("运动");
    7. }
    8. }
    9. class Dog extends Animal{
    10. //子类必须实现父类的抽象方法,否则编译报错
    11. public void shout(){
    12. System.out.println("旺旺旺");
    13. }
    14. }
    15. public class Test {
    16. public static void main(String[] args) {
    17. Dog d = new Dog();
    18. d.shout();//调用子类的实现
    19. }
    20. }

    二、接口(Interface)

    1.接口的概念

    抽象类是从多个类中抽象出来的模板,如果将这种抽象进行得更彻底,则可以提炼出一种更加特殊的“抽象类”——接口,接口定义的是一组规范,接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要,可见接口是从多个相似类中抽象出来的规范,这样可以更加规范地对子类进行约束,全面地专业地实现了规范和具体实现的分离。如:Type-c接口是指遵守了Type-C规范的插槽,而具体的Type-c充电插槽只是Type-C接口的实例,只要遵守这个接口规范,就可以保证能正常充电。面向对象的精髓,就是对对象的抽象,最能体现这点的就是接口。
    抽象类中还提供某些具体的实现,而接口不提供任何具体实现,接口中的所有方法都是抽象方法,接口完全是面向规范的。接口是定义两个模块之间的通信标准,通信间的规范,如果把设计的模块间的接口定义后,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现。

    2.三种类的区别

  • 普通类:具体实现

  • 抽象类:具体实现+规范(抽象方法)
  • 接口:规范

    3.接口的定义

  • Java9以前的版本

    1. [修饰符] interface 接口名 [extends 父接口1,父接口2...]{
    2. 零个到多个常量定义;
    3. 零个到多个抽象方法定义;
    4. }

    定义接口的详细说明:
    修饰符:只能是public或默认
    接口名:和类名相同命名机制
    extends:接口可以多继承
    常量:接口中的属性只能是常量,总是public static final,可省略,默认就是常量
    方法:接口中的方法只能是public abstract ,可省略,默认为抽象方法

    1. //飞行接口
    2. interface Volant{
    3. int FLY_HEIGHT = 100;//总是public static final类型的
    4. void fly();//总是public abstract void fly()类型的
    5. }
    6. //善良接口
    7. interface Kind{
    8. void helpOthers();
    9. }
    10. //子类通过implements实现接口中的规范
    11. class Angle implements Volant,Kind{
    12. //一定要具体实现fly()方法
    13. public void fly() {
    14. System.out.println("飞飞飞");
    15. }
    16. //一定要具体实现helpOthers()方法
    17. public void helpOthers() {
    18. System.out.println("路见不平,拔刀相助");
    19. }
    20. }

    4.要点

  • 子类通过implements实现接口中的规范

  • 接口不能创建实例,但是可以用来声明引用变量
  • 一个类实现了接口,必须实现接口中的所有方法
  • JDK1.8之前(不含8),接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
  • JDK1.8(含8)以后,接口中可包含普通的静态方法、默认方法,Java9为接口增加了一种私有方法,私有方法的主要作用就是作为工具方法,私有方法可以是类方法也可以是实例方法,为接口中的类方法或默认方法提供支持。类方法、默认方法、私有方法都可以有具体的实现。

    5.接口中的静态方法和默认方法(Java8以后)

    5.1 默认方法

    Java8(包含8)以后,允许给接口添加一个非抽象的方法实现,只需要使用default关键字修饰即可,这个方法叫做默认方法,也叫扩展方法。
    默认方法和抽象方法的区别是抽象方法要被实现,而默认方法是在接口里直接实现了,不需要再被实现,当然默认方法可以被继承重写。
    注:默认方法和前面的default方法的区别,方法如果不加任何修饰就为default方法,而接口中的默认方法要显式加default修饰。

    1. interface A{
    2. //显式使用default修饰
    3. default void moren(){
    4. System.out.println("我是A接口的默认方法");
    5. }
    6. }
    7. public class Test implements A{
    8. @Override
    9. public void moren(){
    10. System.out.println("重写A接口的默认方法");
    11. }
    12. public static void main(String[] args) {
    13. }
    14. }

    5.2 静态方法

    Java8以后,可以在接口中定义静态方法的实现,这个静态方法从属于接口(接口是一种特殊的类),可以通过接口名调用。如果子类中定义同名的静态方法,则是完全不相同的两个方法,从属于子类,通过子类名调用。

    1. interface A{
    2. public static void staticMethod(){
    3. System.out.println("A中的静态方法");
    4. }
    5. }
    6. class B implements A{
    7. public static void staticMethod(){
    8. System.out.println("B中的静态方法");
    9. }
    10. }
    11. public class Test {
    12. public static void main(String[] args) {
    13. A.staticMethod();
    14. B.staticMethod();
    15. }
    16. }
    17. //A中的静态方法
    18. //B中的静态方法

    Day6 - 图1

    5.3 默认方法和静态方法的区别

    在默认方法中可以直接调用静态方法,而静态方法不能调用默认方法,默认方法就是一个普通方法。(静态成员不能访问非静态成员)

    1. interface A{
    2. public static void staticMethod(){
    3. System.out.println("A中的静态方法");
    4. //moren();//报错
    5. }
    6. default void moren(){
    7. staticMethod();//可调用静态方法
    8. }
    9. }

    6. 接口的多继承

    接口完全支持多继承,子接口扩展某个父接口,将会获得父接口所定义的一切。

    1. interface A{
    2. void testa();
    3. }
    4. interface B{
    5. void testb();
    6. }
    7. interface C extends A, B{
    8. void testc();
    9. }
    10. //必须实现C中所有的抽象方法
    11. public class Test implements C {
    12. @Override
    13. public void testa() {
    14. }
    15. @Override
    16. public void testb() {
    17. }
    18. @Override
    19. public void testc() {
    20. }
    21. }

    image.png

    三、String类

    1.String类基础

  • String类又称为不可变字符序列。

  • String类位于java.lang包中,Java程序默认导入java.lang包下的所有类。
  • Java字符串就是Unicode字符序列,例如“Java”就是4个Unicode字符‘J’‘a’‘v’‘a’组成的。但要注意,“Java”字符串是一个对象,它同样有很多的方法、属性,不是只有这四个字符,所以一个字符串所占的空间实际上是很大的。
  • Java没有内置的字符串类型,而是提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例。

    2.字符串相等判断(使用equals方法)

    1. public class Test {
    2. public static void main(String[] args) {
    3. String str1 = "abcde";
    4. String str2 = new String("abcde");
    5. String str3 = "abcde";
    6. System.out.println(str1 == str2);//默认使用了toSting()方法
    7. System.out.println(str1 == str3);
    8. System.out.println(str1);//String类的toSting方法被重写了,这里打印输出的不再是地址
    9. System.out.println(str2);
    10. System.out.println(str3);
    11. }
    12. }
    13. //false
    14. //true
    15. //abcde
    16. //abcde
    17. //abcde
    Day6 - 图3
    String的对象中用一个Byte数组来存储字符,当然String对象中还有其他属性和方法,所以str1和str2指向的不是同一个对象。但我们比较字符串的时候,一般只要字符串的内容相同就认为是相等的,涉及到字符串比较可以使用equals方法。
    1. public class Test {
    2. public static void main(String[] args) {
    3. String str1 = "abcde";
    4. String str2 = new String("abcde");
    5. System.out.println(str1.equals(str2));//使用equals则只比较字符串内容
    6. }
    7. }
    8. //true

    3.常量池原理

    4.String类常用方法详解(本章重点学习这些方法如何使用,多练习)

    String类是我们最常使用的类,字符串类的方法我们必须非常熟悉。以下是常用的方法:
方法 解释说明
char charAt(int index) 返回字符串中第index个字符
boolean equals(String other) 判断两个字符串是否相等
boolean equalsIgnoreCase(String other) 判断两个字符串是否相等(忽略大小写)
int indexOf(String str) 从头到尾查找,返回找到的第一个子字符串的索引位置,如未找到,返回-1。可以用来判断字符串中是否包含该子串
lastIndexOf() 从尾开始查找,返回找到的第一个子字符串的索引位置,如未找到,返回-1。
int length() 返回字符串的长度
String replace(char oldChar, char newChar) 返回一个新串,它是用newChar替换所有的oldChar生成的新的字符串
boolean startWith(String str) 判断字符串是否以str开头
boolean endsWith(String str) 判断字符串是否以str结尾
String substring(int beginIndex) 截取从开始索引到字符串末尾的字符串
String substring(int beginIndex, int endIndex) 截取从开始索引到结束索引间的字符串,其中包含开始索引这个字符,但不包括结束索引的字符
String toLowerCase() 返回一个新字符串,该串将原字符串的大写字母改成小写字母
Stirng toUpperCase() 返回一个新字符串,该串将原字符串的小写字母改成大写字母
String trim() 返回一个新字符串,该串删除了原串头部和尾部的空格

注:String是不可变字符串,所有的替换、截取、去空格、转换大小写都是生成新字符串,原来的字符串不会变化。

  1. public class Test {
  2. public static void main(String[] args) {
  3. String str1 = "abcdefdef";
  4. String str2 = "Abcdefdef";
  5. System.out.println(str1.charAt(0));//a
  6. System.out.println(str1.length());//9
  7. System.out.println(str1.charAt(str1.length() - 1));//f
  8. System.out.println(str1.equals(str2));//false
  9. System.out.println(str1.equalsIgnoreCase(str2));//true
  10. System.out.println(str1.indexOf("def"));//3
  11. System.out.println(str1.lastIndexOf("def"));//6
  12. System.out.println(str1.replace("def", "ABC"));//abcABCABC
  13. System.out.println(str1.startsWith("abc"));//true
  14. System.out.println(str1.endsWith("def"));//true
  15. System.out.println(str1.substring(1));//bcdefdef
  16. System.out.println(str1.substring(3,5));//de, 3~(5-1)
  17. System.out.println(str1.toUpperCase());//ABCDEFDEF
  18. System.out.println(str2.toLowerCase());//abcdefdef
  19. String str3 = " a b ";
  20. System.out.println(str3.trim());//a b
  21. System.out.println(str3.replace(" ", ""));//ab,去除所有空格
  22. System.out.println(str1.replace("def", ""));//abc,删除def
  23. }
  24. }