Posts java 基础(二)
Post
Cancel

java 基础(二)

day6 类与对象,封装,构造方法

类与对象

  • 面向对象 当需要实现一个功能的时候,不关心具体的步骤,而是找一个已经具有该功能的人,来帮我做事儿。
  • 面向过程 当需要实现一个功能的时候,每一个具体的步骤都要亲力亲为,详细处理每一个细节。

  • 类 :是一组相关属性和行为的集合。可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
  • 类的属性与行为: 略。
  • 类的定义:就是定义类的成员,包括成员变量和成员方法
  • 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
  • 成员方法:和以前定义方法几乎是一样的。只不过把 static 去掉。

对象

  • 对象:是一类事物的具体体现。对象是类的一个实例,必然具备该类事物的属性 和行为.

类的定义格式

  • 定义类,就是定义类的成员,包括成员变量成员方法
  • 成员变量:跟定义变量几乎一样,唯一不同的是位置发生了改变,在类中,方法外
  • 成员方法: 和以前定义方法几乎是一样的,只是把static去掉
  • 类的定义格式举例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    public class Student {
     String name;
     int age ;
     public void study(){
       System.out.println("好好学习天天向上");
     }
       public void eta(){
       System.out.println("学习饿了要吃饭");
     }
    }
    

对象的使用

  • 通常情况下,一个类并不能直接使用,需要根据类创建一个对象,才能使用。
      1. 导包:也就是指出需要使用的类,在什么位置。import 包名称.类名称;
      1. 创建,格式: 类名称 对象名 = new 类名称(); Student stu = new Student();
      1. 使用,分为两种情况: 使用成员变量:对象名.成员变量名 使用成员方法:对象名.成员方法名(参数)
  • 举例说明如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    public class Demo02Student {
    
        public static void main(String[] args) {
            // 1. 导包。
            // 我需要使用的Student类,和我自己Demo02Student位于同一个包下,所以省略导包语句不写
    
            // 2. 创建,格式:
            // 类名称 对象名 = new 类名称();
            // 根据Student类,创建了一个名为stu的对象
            Student stu = new Student();
    
            // 3. 使用其中的成员变量,格式:
            // 对象名.成员变量名
            System.out.println(stu.name); // null
            System.out.println(stu.age); // 0
            System.out.println("=============");
    
            // 改变对象当中的成员变量数值内容
            // 将右侧的字符串,赋值交给stu对象当中的name成员变量
            stu.name = "赵丽颖";
            stu.age = 18;
            System.out.println(stu.name); // 赵丽颖
            System.out.println(stu.age); // 18
            System.out.println("=============");
    
            // 4. 使用对象的成员方法,格式:
            // 对象名.成员方法名()
            stu.eat();
            stu.sleep();
            stu.study();
        }
    
    }
    
  • 成员变量的默认值 成员变量跟数组一样有默认值,具体表格如下。

对象使用的内存图。

  • 堆区
  1. 存储的全部是对象,每个对象都包含一个与之对应的 class 的信息。(class 的目的是得到操作指令)
  2. jvm 只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 。
  • 栈区
  1. 每个线程包含一个栈区,栈中只保存基础数据类型的值和对象以及基础数据的引用
  2. 每个栈中的数据(基础数据类型和对象引用)都是私有的,其他栈不能访问。
  3. 栈分为 3 个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
  • 方法区
  1. 又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的 class 和 static 变量。
  2. 方法区中包含的都是在整个程序中永远唯一的元素,如 class,static 变量。
  • 程序执行基本流程
  1. 方法区包含所有的class文件,里面包含成员变量(实例变量) 以及成员方法。
  2. 找到程序入口main方法,main方法压栈进入栈区。
  3. 栈中的基础数据与对象引用对每个线程都是私有的。
  4. new 一个对象,对象存入堆中,栈中只存引用。
  5. 关于方法调用的内存图,栈区保存对象的引用地址,调用某个对象的方法,通过地址找到堆区中的实例对象,再通过对象找到方法区中的成员方法。
  • 只有一个对象的内存图

  • 两个对象使用同一个方法的内存图

  • 两个引用只想同一个对象的内存图

  • 使用对象类型作为方法的参数

  • 使用对象类型作为方法的返回值

成员变量与局部变量的区别

1
2
3
4
5
6
7
public class Car{
  String color;    // 成员变量
  public void drive(){
    int speed = 80;  // 局部变量
    System.out.ptintln("时速"+speed);
  }
}
  • 在类中的位置不同
    • 成员变量:类中,方法外
    • 局部变量: 方法中
  • 作用范围不一样
    • 成员变量:类中
    • 局部变量:方法中
  • 初始化值不同
    • 成员变量:有默认值
    • 局部变量:没有默认值。必须先定义,赋值,最后使用

封装

  • 原则:先将属性隐藏起来,若要访问某个属性,提供公共的方法对其访问
  • 步骤
    • 使用 private 关键字来修饰成员变量
    • 对需要访问的成员对量,提供对应的 getXxx 方法和 setXxx 方法。

封装操作

以代码举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  public class Person {

  String name; // 姓名
  private int age; // 年龄

  public void show() {
      System.out.println("我叫:" + name + ",年龄:" + age);
  }

  // 这个成员方法,专门用于向age设置数据
  public void setAge(int num) {
      if (num < 100 && num >= 9) { // 如果是合理情况
          age = num;
      } else {
          System.out.println("数据不合理!");
      }
  }

  // 这个成员方法,专门私语获取age的数据
  public int getAge() {
      return age;
  }

}


public class Demo03Person {

  public static void main(String[] args) {
      Person person = new Person();
      person.show();

      person.name = "xyx";
//        person.age = -20; // 直接访问private内容,错误写法!
      person.setAge(20);
      person.show();
  }
}
  • 封装操作的优化 1 –this 关键字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      public void sayHello(String name) {
        System.out.println(name + ",你好。我是" +this.name);
        System.out.println(this);
     }
    
    
          public static void main(String[] args) {
          Person person = new Person();
          // 设置我自己的名字
          person.name = "王健林";
          person.sayHello("王思聪");
    
          System.out.println(person); // 地址值
      }
    
    
    
    
    • 方法被哪个对象调用,方法中的 this 就代表那个对象。即谁在调用,this 就代表谁。

封装优化操作 2 – 构造方法

  • 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样
  • 构造方法不要写返回值类型,连 void 都不写
  • 构造方法不能 return 一个具体的返回值
  • 如果没有编写任何构造方法,那么编译器将会默认赠送一个构造方法,没有参数、方法体什么事情都不做。 例: public Student() {}
  • 一旦编写了至少一个构造方法,那么编译器将不再赠送。
  • 构造方法也是可以进行重载的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Student {
  // 成员变量
  private String name;
  private int age;
  // 无参数的构造方法
  public Student() {
      System.out.println("无参构造方法执行啦!");
  }
  // 全参数的构造方法
  public Student(String name, int age) {
      System.out.println("全参构造方法执行啦!");
      this.name = name;
      this.age = age;
  }
}

标准代码 javabean

  • 一个标准的类通常要拥有下面四个组成部分:
    1. 所有的成员变量都要使用 private 关键字修饰
    2. 为每一个成员变量编写一对儿 Getter/Setter 方法
    3. 编写一个无参数的构造方法
    4. 编写一个全参数的构造方 可使用 Alt + insert 快速创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Student {
  private String name; // 姓名
  private int age; // 年龄
  public Student() {
  }
  public Student(String name, int age) {
      this.name = name;
      this.age = age;
  }
  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;
  }
}

day7 Scanner 类、Random 类、ArrayList 类

API 简述

API(Application Programming Interface),应用程序编程接口。Java API 是一本程序员的 字典 ,是 JDK 中提供给我们使用的类的说明文档。这些类将底层的代码实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可。

screen 类 与 random 类

引用类型的一般步骤:

  • 导包 import 包名.类名;
  • 创建对象 数据类型变量名=new 数据类型(参数列表);
  • 调用方法变量名.方法名();

screen 的基本用法

1
2
3
4
5
6
7
8
9
import java.util.Scanner;
public class demo {
  public stasitc void main (String[] args){
    Screen sc = new Screen(System.in)

    int num = sc.nextlnt ();// 获取数字
    String str = sc.next(); // 获取字符串
  }
}

random 的基本用法

1
2
3
import java.util.Random;
Random r = new Random();
int number = r.nextInt(10);

ArrayList 类

引入对象数组

使用学生数组,存储三个学生对象

1
2
3
4
public class Student {
}

Student[] students = new Student[3];

ArrarList 类

  • 几点基础:

    • 数组的长度不可改变,但 Arrylist 的长度是可以随意变化的
    • 对于 Arrylist 来说,代表泛型
      • 泛型的意义:集合中的所有元素,全都是统一类型
      • 泛型只能是引用型,不能是基本的数据类型
    • 对与 Arrylist 来说,直接打印不是得到地址而是内容。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      import java.util.ArrayList;
      ArrayList<String> list = new ArrayList<>();
      list.add("xyx"); // 向列表中增加元素
      
      String name = list.get(2); // 从集合中读取元素
      
      String name-1 = list.remove(3); // 删除集合元素
      
      int size = list.size(); // 取集合长度
      
  • 如何存储基本类型

    1
    2
    3
    
     ArrayList<Integer> list = new ArrayList<Integer>();
     list.add(1);
     list.add(2);
    

day 8 String 类、static 关键字、Arrays 类、 Math 类

String 类

字符串的特点

  • 字符串的内容永不可变。【重点】
  • 正是因为字符串不可改变,所以字符串是可以共享使用的。
  • 字符串效果上相当于是 char[] 字符数组,但是底层原理是 byte[]

创建字符串的四种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
  // 使用空参构造
  String str1 = new String();

  // 根据字符数组创建字符串
  char[] charArray = { 'A', 'B', 'C' };
  String str2 = new String(charArray);

  // 根据字节数组创建字符串
  byte[] byteArray = { 97, 98, 99 };
  String str3 = new String(byteArray);

  // 直接创建
  String str4 = "hello world";

字符串常量池:程序当中直接写上的双引号字符串,就在字符串常量池中。 内存划分如下:

几种常用方法

  • 比较字符串内容是否相等

    1
    2
    
    str1.equals(str2) //要比较大小写
    strAequalsIgnoreCase(strB)  // 直接忽略大小写
    
    • 几点注释
      • equals 方法具有对称性,也就是 a.equals(b)和 b.equals(a)效果一样。
      • 推荐:”abc”.equals(str) 不推荐:str.equals(“abc”),因为空值 null 会报错。
  • 字符串的长度

` string.length();`

  • 获取指定位置的单个字符

string.charAt(1)

  • 查找参数字符串在本来字符串当中出现的第一次索引位置

    1
    2
    
          String original = "HelloWorldHelloWorld";
          int index = original.indexOf("llo");
    
  • 截取字符串的方法

    1
    2
    3
    4
    
    String str1
    public String substring(int index)截取从参数位置一直到字符串末尾返回新字符串
    public String substring(int begin, int end)截取从begin开始一直到end结束中间的字符串
    备注[begin,end)包含左边不包含右边
    
  • 几种转换

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    //转换成字符数组
    char[] chars = "Hello".toCharArray();
    
    // 转换成字节数组
    byte[] bytes = "abc".getBytes();
    
    // 字符串内容替换
    String str1 = "How do you do?";
    String str2 = str1.replace("o", "*");
    
    // 字符串的分割
    String str1 = "aaa,bbb,ccc";
    String[] array1 = str1.split(","); //以句号为分割,正则表达式
    

static 关键字 ⭐⭐

定义与使用

类变量

  • 当 static 修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改 该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。
  • 定义格式 static 数据类型 变量名;

静态方法

  • 当 static 修饰成员方法时,该方法称为类方法 。静态方法在声明中有 static ,建议使用类名来调用,而不需要 创建类的对象。调用方式非常简单。
  • 静态方法的使用: 直接类名称.方法名调用
  • 基本格式如下

    1
    2
    3
    4
    
    static int numStatic; // 静态变量
    public static void methodStatic() {
    System.out.println(numStatic);
    }
    
  • 几点注意:

    • 静态不能直接访问非静态。
    • 静态方法当中不能用 this。
  • 静态的内存图

  • 静态代码块 当第一次用到本类时,静态代码块执行唯一的一次。静态内容总是优先于非静态,所以静态代码块比构造方法先执行。
    1
    2
    3
    
    static {
    System.out.println("静态代码块执行!");
      }
    

Arrays 类

1
2
3
4
5
6
7
 int[] intArray = {10, 20, 30};

 //将参数数组变成字符串(按照默认格式:[元素1, 元素2, 元素3...])
 String intStr = Arrays.toString(intArray);//[10, 20, 30]

 Arrays.sort(intArray); //对数组进行排序

math 类

1
2
3
4
System.out.println(Math.abs(3.14)); //取绝对值
System.out.println(Math.ceil(3.9)); // 向上取整
System.out.println(Math.floor(30.1));  //向下取整
System.out.println(Math.round(20.4)) //四舍五入

继承 super this 抽象类

继承

  • 定义 :就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。

继承的基本格式

1
2
3
4
5
6
7
public class 父类名称 {
  //...
}

public class 子类名称 extends 父类名称{

}

继承后的成员变量

  • 调用规则

    • 直接通过子类对象访问成员变量: 等号左边是谁,就优先用谁,没有则向上找。
    • 间接通过成员方法访问成员变量: 该方法属于谁,就优先用谁,没有则向上找。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    // 局部变量:         直接写成员变量名
    // 本类的成员变量:    this.成员变量名
    // 父类的成员变量:    super.成员变量名
    
    public class Zi extends Fu {
    
        int num = 20;
    
        public void method() {
            int num = 30;
            System.out.println(num); // 30,局部变量
            System.out.println(this.num); // 20,本类的成员变量
            System.out.println(super.num); // 10,父类的成员变量
        }
    
    

继承后的成员方法

  • 两组概念

    • 重写(Override):方法的名称一样,参数列表【也一样】。覆盖、覆写。
    • 重载(Overload):方法的名称一样,参数列表【不一样】。
  • 几点注意事项
    • @Override:写在方法前面,用来检测是不是有效的正确覆盖重写。
      1
      2
      3
      4
      
      @Override
      public String method() {
        return null;
      }
      
    • 子类方法的返回值必须【小于等于】父类方法的返回值范围。object 为最高子类
    • 子类方法的权限必须【大于等于】父类方法的权限修饰符。public > protected > (default) > private
  • 重写的应用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    public class Phone {
      public void send() {
          System.out.println("发短信");
      }
      public void show() {
          System.out.println("显示号码");
      }
    }
    
    public class NewPhone extends Phone {
        @Override
        public void show() {
            super.show();    // 直接继承父类
            System.out.println("显示姓名");
            System.out.println("显示头像");
        }
    }
    

继承后的特点—构造方法 【重难点】

  • 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
  • 子类的构造方法中默认有一个 super() ,表示调用父类的构造方法
  • 子类必须调用父类构造方法,不写则赠送 super();写了则用写的指定的 super 调用,super 只能有一个,还必须是第一个。 结合代码说明如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    public class Fu {
      public Fu() {
          System.out.println("父类无参构造");
      }
      public Fu(int num) {
          System.out.println("父类有参构造!");
      }
    }
    public class Zi extends Fu {
        public Zi() {
            super(); // 在调用父类无参构造方法
    //        super(20); // 在调用父类重载的构造方法
            System.out.println("子类构造方法!");
        }
        public void method() {
    //        super(); // 错误写法!只有子类构造方法,才能调用父类构造方法。
        }
    }
    

super 关键字与 this 关键字

  • super :代表父类的存储空间标识(可以理解为父亲的引用)。
  • this :代表当前对象的引用(谁调用就代表谁)。

两种使用方法

  • 访问成员 略
  • 访问构造方法 ⭐⭐ 子类的每个构造方法中均有默认的 super(),调用父类的空参构造。手动调用父类构造会覆盖默认的 super()。 super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
    1
    2
    3
    4
    5
    
    public Zi() {
    //super(); // 这一行不再赠送
    this(123); // 本类的无参构造,调用本类的有参构造
    //this(1, 2); // 错误写法!
      }
    

继承的特点

  • Java 只支持单继承,不支持多继承。
  • Java 支持多层继承(继承体系)。

抽象类

  • 抽象方法:就是加上 abstract 关键字,然后去掉大括号,直接分号结束。public abstract void run();
  • 抽象类:抽象方法所在的类,必须是抽象类才行。在 class 之前写上 abstract 即可。public abstract class Animal{}

抽象类的一般使用方法

  • 不能直接创建 new 抽象类对象。
  • 必须用一个子类来继承抽象父类。
  • 子类必须覆盖重写抽象父类当中所有的抽象方法。⭐⭐
    • 可以有如下情况
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      public abstract class A1{
        void1();
        void2();
      }
      public abstract class A2 extends A1{
        @override
        void1();
      }
      public abstract class A3 extends A2{
        @override
        void2();
      }
      
  • 创建子类对象进行使用。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    // 创建抽象父类
    public abstract class fu{
      // 在父类创建一个抽象方法
      public abstract void eat();
    }
    // 创建一个子类继承抽象父类
    public class Zi extends Animal {
      @overridw
      public void eat(){
        System.out.println("xyx");
      }
    }
    //主函数
    public class DemoMain {
    public static void main(String[] args) {
    //Animal animal = new Animal(); // 错误写法!不能直接创建抽象类对象
    Cat cat = new Cat();
    cat.eat();
      }
    }
    

一个综合案例–群发红包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// 创建主类
public class User {
  private String name;
  private int money;
  public User() {
  }
  public User(String name, int money) {
      this.name = name;
      this.money = money;
  }
  public void show() {
      System.out.println("我叫:" + name + ",我有多少钱:" + money);
  }
  public String getName() {
      return name;
  }
  public void setName(String name) {
      this.name = name;
  }
  public int getMoney() {
      return money;
  }
  public void setMoney(int money) {
      this.money = money;
  }
}

// 创建成员类
public class Member extends User {
  public Member() {
  }
  public Member(String name, int money) {
      super(name, money);
  }
  public void receive(ArrayList<Integer> list) {
      int index = new Random().nextInt(list.size());
      int delta = list.remove(index);
      int money = super.getMoney();
      super.setMoney(money + delta);
  }
}

//发红包的人
public class Manager extends User {
  public Manager() {
  }
  public Manager(String name, int money) {
      super(name, money);
  }
  public ArrayList<Integer> send(int totalMoney, int count) {
      ArrayList<Integer> redList = new ArrayList<>();
      int leftMoney = super.getMoney();
      if (totalMoney > leftMoney) {
          System.out.println("余额不足");
          return redList;
      }
      super.setMoney(leftMoney - totalMoney);
      int avg = totalMoney / count;
      int mod = totalMoney % count;
      for (int i = 0; i < count - 1; i++) {
          redList.add(avg);
      }
      int last = avg + mod;
      redList.add(last);
      return redList;
  }
}

// 主函数
public class MainRedPacket {
  public static void main(String[] args) {
      Manager manager = new Manager("群主", 100);

      Member one = new Member("成员A", 0);
      Member two = new Member("成员B", 0);
      Member three = new Member("成员C", 0);

      manager.show();
      one.show();
      two.show();
      three.show();
      System.out.println("===============");
      ArrayList<Integer> redList = manager.send(20, 3);
      one.receive(redList);
      two.receive(redList);
      three.receive(redList);
      manager.show();
      one.show();
      two.show();
      three.show();
  }
}

接口与多态

接口

  • 定义: 简单来说,接口就是多个类的公共规范。 接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做 是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
  • 接口的使用步骤

    1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口。
    2. 接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
    3. 创建实现类的对象,进行使用。
  • 接口的定义:
    1
    2
    3
    
    public interface 接口名称 {
      // 接口内容
    }
    
  • 接口中可以包含的内容
    1. 常量
    2. 抽象方法
    3. 默认方法
    4. 静态方法
    5. 私有方法

接口的抽象方法

在任何 java 版本中,接口都能定义抽象方法。

1
2
3
4
public inerface MyAbstract {
  public abstract void methodAbs1();
  public abstract void methodAbs2();
}

几点注意:

  • 接口当中的抽象方法,修饰符必须是两个固定的关键字:public abstract
  • 这两个关键字修饰符,可以选择性地省略。
  • 方法的三要素,可以随意定义。

具体步骤如下:

//定义接口
public interface live {
  public abstract void eat();
}

//定义实现类
public class Animal implements live {
  @override
  public void eat(){
    System.out.println("hello");
  }
}
// 定义测试类
public class demo {
  public stastic void main(String[] args){
    Animal a = new Animal();
    a.eat();
  }
}

接口的默认方法

  • 定义:从 java8 开始,接口允许定义默认方法,主要格式为: public default 返回值类型 方法名称(参数列表) { //方法体 }
  • 几点注意:

    • 接口的默认方法,可以通过接口实现类对象,直接调用。
    • 接口的默认方法,也可以被接口实现类进行覆盖重写。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    // 接口的定义
    public interface MyInterfaceDefault {
      // 抽象方法
      public abstract void methodAbs();
      // 默认方法
      public default void methond_default();
    }
    
    // 接口的继承与重写
    public class MyInterfaceDefaultA implements MyInterfaceDefault {
      @Override
      public void methodAbs() {
          System.out.println("实现了抽象方法,AAA");
      }
    } // 默认方法可以直接继承,也可以覆盖重写。
    
    // 定义测试类
    // 略
    

接口的静态方法

定义: 不能通过接口实现类的对象来调用接口当中的静态方法。通过接口名称,直接调用其中的静态方法,也无法重写静态方法。

1
2
3
4
5
6
7
8
// 接口的定义方式
public interface MyInterfaceStatic {

  public static void methodStatic() {
      System.out.println("这是接口的静态方法!");
  }

}

一个小例子,引出私有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//接口的定义
public interface MyInterfacePrivateB {
  public static void methodStatic1() {
      System.out.println("静态方法1");
      methodStaticCommon();
  }
  public static void methodStatic2() {
      System.out.println("静态方法2");
      methodStaticCommon();
  }
  //私有方法使用
  private static void methodStaticCommon() {
      System.out.println("AAA");
      System.out.println("BBB");
      System.out.println("CCC");
  }
}
// 接口的调用测试
public class Demo04Interface {
  public static void main(String[] args) {
      MyInterfacePrivateB.methodStatic1();
      MyInterfacePrivateB.methodStatic2();
      // 错误写法!
//        MyInterfacePrivateB.methodStaticCommon();
  }
}

私有方法

  • 问题描述:我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。但是这个共有方法不应该让实现类使用,应该是私有化的。
  • 两个分类
    • 普通私有方法,解决多个默认方法之间重复代码问题 private 返回值类型 方法名称(参数列表) { 方法体 }
    • 静态私有方法,解决多个静态方法之间重复代码问题 private static 返回值类型 方法名称(参数列表) { 方法体 }

接口中定义“成员变量”的方法

  • 定义
    1
    2
    3
    4
    
    public interface MyInterfaceConst {
      // 这其实就是一个常量,一旦赋值,不可以修改
      public static final int NUM_OF_MY_CLASS = 12;
    }
    
  • 几点注意
    • 接口当中的常量,可以省略 public static final,注意:不写也照样是这样。
    • 接口当中的常量,必须进行赋值;不能不赋值。
    • 接口中常量的名称,使用完全大写的字母,用下划线进行分隔。

接口的多体现

在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接 口的多实现。并且,一个类能继承一个父类,同时实现多个接口。 几点注意:

  • 接口是没有静态代码块或者构造方法的。
  • 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
  • 1
    2
    3
    4
    
    class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... {   
        // 重写接口中抽象方法【必须】   
       // 重写接口中默认方法【不重名时可选】   
    }
    
  • 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
  • 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
  • 如果实现类锁实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
  • 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先用父类当中的方法。⭐⭐

抽象方法

接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次。

默认方法

接口中,有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次。

静态方法

接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。

优先级问题

当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的默认方法重名,子类就近选择执 行父类的成员方法。

接口的多继承问题

一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继 承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次。

多态

  • 多态的前提

    • 继承或者实现【二选一】
    • 方法的重写【意义体现:不重写,无意义】
    • 父类引用指向子类对象【格式体现】
  • 一个例子 格式: 父类名称 对象名 = new 子类名称(); 或者: 接口名称 对象名 = new 实现类名称();

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    // 父类
    public class Fu {
      public void method() {
          System.out.println("父类方法");
      }
      public void methodFu() {
          System.out.println("父类特有方法");
      }
    }
    
    // 子类
    public class Zi extends Fu {
      @Override
      public void method() {
          System.out.println("子类方法");
      }
    }
    
    //主测试函数
    public class Demo01Multi {
      public static void main(String[] args) {
          // 使用多态的写法
          // 左侧父类的引用,指向了右侧子类的对象
          Fu obj = new Zi();
          obj.method();
          obj.methodFu();
      }
    }
    

    当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写 后方法。

多态的访问原则

  • 访问成员变量的两种方式:
    • 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
    • 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
  • 在多态的代码当中,成员方法的访问规则是:

    • 看 new 的是谁,就优先用谁,没有则向上找。
  • 口令可总结为: 成员变量:编译看左边,运行还看左边。 成员方法:编译看左边,运行看右边。

多态的好处

引用类型转换

多态的转型分为向上转型和向下转型两种

  • 向上转型 多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。

    • 使用格式:

      1
      
      父类类型  变量名 = new 子类类型(); Animal a = new Cat();
      
    • 一个弊端 向上转型一定是安全的,没有问题的,正确的。但是也有一个弊端: 对象一旦向上转型为父类,那么就无法调用子类原本特有的内容

  • 向下转型

    • 为什么要向下转型? 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥 有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子 类特有的方法,必须做向下转型。

    父类类型向子类类型向下转换的过程,这个过程是强制的。

    1
    2
    3
    4
    5
    6
    
    Animal animal = new Cat(); // 本来创建的时候是一只猫
    animal.eat(); // 猫吃鱼
    //animal.catchMouse(); // 错误写法!属于子类特有方法
    // 向下转型,进行“还原”动作
    Cat cat = (Cat) animal;
    cat.catchMouse(); // 猫抓老鼠
    
  • 转型异常的问题 引用instanceof函数

    1
    2
    3
    4
    
    if (animal instanceof Cat) {
      Cat cat = (Cat) animal;
      cat.catchMouse();
    }
    

final、权限、内部类、引用类型

final

  • final 常见的地中用法

    • 可以用来修饰一个类
    • 可以用来修饰一个方法
    • 还可以用来修饰一个局部变量
    • 还可以用来修饰一个成员变量
  • 用来修饰类

    当 final 关键字用来修饰一个类的时候,格式:

    1
    2
    3
    
    public final class 类名称 {
      // ...
    }
    

    注意:一个类如果是 final 的,那么其中所有的成员方法都无法进行覆盖重写(因为没儿子。)

  • 用来修饰一个方法 当 final 关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写。

    1
    2
    3
    
    修饰符 final 返回值类型 方法名称(参数列表) {
      // 方法体
    }
    

    ⭐⭐ 对于类、方法来说,abstract 关键字和 final 关键字不能同时使用,因为矛盾。

  • 用来修饰一个局部变量 “一次赋值,终生不变” final int num = 30

    • 对于基本类型来说,不可变说的是变量当中的数据不可改变
    • 对于引用类型来说,不可变说的是变量当中的地址值不可改变
      1
      2
      3
      4
      
      final Student stu2 = new Student("高圆圆");
      System.out.println(stu2.getName()); // 高圆圆
      stu2.setName("高圆圆圆圆圆圆");
      System.out.println(stu2.getName()); // 高圆圆圆圆圆圆
      
  • 用来修饰一个成员变量 对于成员变量来说,如果使用 final 关键字修饰,那么这个变量也照样是不可变。

    • 由于成员变量具有默认值,所以用了 final 之后必须手动赋值,不会再给默认值了。
    • 对于 final 的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
    • 必须保证类当中所有重载的构造方法,都最终会对 final 的成员变量进行赋值。

权限修饰符

  • java 的四种权限符的访问能力

内部类

  • 定义:将一个类 A 定义在另一个类 B 里面,里面的那个类 A 就称为内部类,B 则称为外部类。
  • 分类:
    • 成员内部类
    • 局部内部类(包含匿名内部类)

成员内部类

  • 举例

    1
    2
    3
    4
    5
    6
    
    修饰符 class 外部类名称 {
      修饰符 class 内部类名称 {
          // ...
      }
      // ...
    }
    
  • 成员内部类的两种使用方式

    • 间接方式:在外部类的方法当中,使用内部类;然后 main 只是调用外部类的方法。
    • 直接方式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
  • 重名问题的解释:

    1
    2
    3
    4
    5
    6
    
      public void methodInner() {
        int num = 30; // 内部类方法的局部变量
        System.out.println(num); // 局部变量,就近原则
        System.out.println(this.num); // 内部类的成员变量
        System.out.println(外部类名称.this.num); // 外部类的成员变量
    }
    

    ⭐⭐ 出现重名现象,格式为 外部类名称.this.外部类成员变量名

局部内部类

  • 定义: 如果一个类是定义在一个方法内部的,那么这就是一个局部内部类。只有当前所属方法才能使用它
  • 基本格式:

    1
    2
    3
    4
    5
    6
    7
    
    修饰符 class 外部类名称 {
      修饰符 返回值类型 外部类方法名称(参数列表) {
          class 局部内部类名称 {
              // ...
          }
      }
    }
    
  • 类的权限修饰符 public > protected > (default) > private 定义一个类的时候,权限修饰符规则:

    1. 外部类:public / (default)
    2. 成员内部类:public / protected / (default) / private
    3. 局部内部类:什么都不能写
  • 几点注意

    • 局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效 final 的】。
    • 原因:
      1. new 出来的对象在堆内存当中。
      2. 局部变量是跟着方法走的,在栈内存当中。
      3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
      4. 但是 new 出来的对象会在堆当中持续存在,直到垃圾回收消失。
  • 例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public void methodOuter() {
      int num = 10; // 所在方法的局部变量
    
      class MyInner {
          public void methodInner() {
              System.out.println(num);
          }
      }
      }
    

匿名内部类

定义

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

  • 格式 接口名称 对象名 = new 接口名称() { // 覆盖重写所有抽象方法 };
  • 对格式“new 接口名称() {…}”进行解析:

    1. new 代表创建对象的动作
    2. 接口名称就是匿名内部类需要实现哪个接口
    3. {…}这才是匿名内部类的内容
  • 几点注意
    1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。 如果希望多次创建对象,而且类的内容一样的话,那么就需要使用单独定义的实现类了。
    2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。 如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
    3. 匿名内部类和匿名对象不是一回事!!! 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】

几个成员变量的扩展

class 作为成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class Hero {
  private String name; // 英雄的名字
  private int age; // 英雄的年龄
  private Weapon weapon; // 英雄的武器
  }
  public Hero(String name, int age, Weapon weapon) {
      this.name = name;
      this.age = age;
      this.weapon = weapon;
  }
  public void attack() {
      System.out.println("年龄为" + age + "的" + name + "用" + weapon.getCode() + "攻击敌方。");
  }
  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;
  }
  public Weapon getWeapon() {
      return weapon;
  }
  public void setWeapon(Weapon weapon) {
      this.weapon = weapon;
  }
}

public class Weapon {
  private String code; // 武器的代号
  public Weapon() {
  }
  public Weapon(String code) {
      this.code = code;
  }
  public String getCode() {
      return code;
  }
  public void setCode(String code) {
      this.code = code;
  }
}

public class DemoMain {
  public static void main(String[] args) {
      // 创建一个英雄角色
      Hero hero = new Hero();
      // 为英雄起一个名字,并且设置年龄
      hero.setName("盖伦");
      hero.setAge(20);
      // 创建一个武器对象
      Weapon weapon = new Weapon("AK-47");
      // 为英雄配备武器
      hero.setWeapon(weapon);
      // 年龄为20的盖伦用多兰剑攻击敌方。
      hero.attack();
  }
}

接口作为成员变量

This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags