本章内容:

1.如何创建一个标准的类
2.API的概述与使用
3.对象型数组
4.面向对象的三大特性
5.抽象类和抽象方法补充
6.接口
7.多态性的补充
8.final关键字

1.如何创建一个标准的类

一个标准的类通常要拥有下面四个部分:

  1. 所有的成员变量都要使用private关键字修饰
  2. 为每一个成员变量编写一堆Getter/Setter方法
  3. 编写一个无参数的构造方法
  4. 编写一个全参数的构造方法

举一个例子:

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
package com.bytedance;

public class Pokers {
public static void main(String[] args) {
// 实例对象创建方式一:
Woman w1 = new Woman();
w1.setName("迪丽热巴");
w1.setAge(18);
System.out.println("姓名:"+w1.getName()+",年龄:"+w1.getAge());
System.out.println("========");

// 实例对象的创建方式二:(推荐,很推荐)
Woman w2 = new Woman("古力娜扎",19);
System.out.println("姓名:"+w2.getName()+",年龄:"+w2.getAge());
System.out.println("========");
// 此时Setter方法也很有用,比如我们想改写一下年纪
w2.setAge(20);
System.out.println("修改年龄后~"+"姓名:"+w2.getName()+",年龄:"+w2.getAge());

}
}

class Woman{
private String name;
private int age;

// 无参数的构造方法
public Woman(){
}

// 全参数的构造方法
public Woman(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;
}
}

运行结果:
姓名:迪丽热巴,年龄:18
========
姓名:古力娜扎,年龄:19
========
修改年龄后~姓名:古力娜扎,年龄:20

2. API的概述与使用

导包:import 包路径.类名称
只有Java.lang包内的内容不需要导入,其余的API都要手动导入

3.对象型数组

对象型数组这一小节不仅能够温习如何创建一个最标准的类,也复习了有关于数组(数组长度不可改变)的一些操作

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
package com.bytedance;

public class objectArray {
public static void main(String[] args){
// 创建一个对象数组,即包含对象的数组
People[] people = new People[3];
// 创建三个People对象实例
People p1 = new People("迪丽热巴", 18);
People p2 = new People("古力娜扎", 28);
People p3 = new People("马尔扎哈", 688);

// 将三个people对象赋予到数组中
people[0] = p1;
people[1] = p2;
people[2] = p3;

System.out.println(people[0]); // 打印地址值:com.bytedance.People@2d98a335
System.out.println(people[1]); // 打印地址值:com.bytedance.People@16b98e56
// 调用对象的方法
people[0].sayHello("陈浩东");
// 大家好,我叫迪丽热巴,今年18岁了,喜欢陈浩东
}
}

// 最标准的类的创建方式如下所示:
class People {
private String name;
private int age;

// 无参构造函数
public People() {
}

// 全参构造函数
public People(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;
}

public void sayHello(String lover) {
System.out.println("大家好,我叫" + this.name + ",今年" + this.age + "岁了,喜欢" + lover);
}

}

4.面向对象的三大特性

面向对象的三大特性:继承,封装,多态
封装性在Java当中的体现:

  1. 方法(理解成python中的函数)就是一种封装
  2. 关键字private也是一种封装

一旦使用了private进行修饰,只能在本类中随意访问,但是,超出了本类范围就不能直接访问(间接访问private成员变量,就是定义一对getter,setter方法)

  • 对于Getter来说,不能有参数,返回值类型与成员变量对应
  • 对于Setter来说,不能有返回值,参数类型和成员变量对应
  • 对于基本类型当中的Boolean值,Getter方法一定要携程isXXXX的形式,而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
public class Main {
public static void main(String[] args) {
Person ming = new Person();
ming.setName("Xiao Ming"); // 设置name
ming.setAge(12); // 设置age
System.out.println(ming.getName() + ", " + ming.getAge());
}
}

class Person {
private String name;
private int age;

public String getName() {
return this.name;
}

public void setName(String name) {
// 对参数name进行校验
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("invalid name");
}
this.name = name.strip(); // 去掉首尾空格
}

public int getAge() {
return this.age;
}

public void setAge(int age) {
// 对参数age进行校验
if (age < 0 || age > 100) {
throw new IllegalArgumentException("invalid age value");
}
this.age = age;
}
}

5.抽象类和抽象方法补充

    如果父类当中的方法不确定能实现什么具体的方法,那么这应该就是一种抽象方法(通俗的理解,比如求图形的面积计算方法,动物吃东西的方法。这里我们具体确定是什么图形,是三角形?还是正方形?,是比较抽象的一类概念)

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

如何使用抽象类和抽象方法:

  1. 不能直接创建new抽象对象
  2. 必须用一个子类来继承抽象父类
  3. 子类必须覆盖重写抽象父类当中的所有抽象方法(即子类去掉抽象方法的abstract关键字,然后补上方法体和大括号),否追编译无法通过
  4. 创建子类对象进行使用
  5. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的

6.接口

接口就是一种公共的规范标准。只要符合标准规范,就可以大家通用(比如插座,U盘)。
接口是一种引用数据类型,最重要的内容就是其中的抽象方法。

如何定义一个接口的格式:

1
2
3
public interface 接口名{
// 接口内容
}

接口内容

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

2.1 常量

接口当中也可以定义“成员变量”,但必须使用public static final三个关键字进行修饰。从效果上看,这就是接口的常量
格式:
public static final 数据类型 常量名称 = 数据值

PS:

  • 一旦使用final关键字,就说明不可变
  • 接口当中的常量必须赋值
  • 接口当中的常量的命名规范:使用完全大写的字母,用下划线进行分隔
  • 调用接口名的常量方法:接口名.常量名

2.2 抽象方法

接口中最重要的时抽象方法
格式:

1
[public] [abstract] 返回值类型 方法名(参数){方法体}

注意:实现类必须覆盖重写接口的所有的抽象方法,除非实现类是抽象的


2.3 默认方法(接口中的默认方法,可以解决接口升级的问题)

1
2
3
4
5
6
7
8
格式:
public default 返回值类型 方法名(参数列表){
方法体 // 默认方法里面可以有方法体
}

使用:
1.接口的默认方法,可以通过接口实现类对象,直接调用
2.接口的默认方法,也可以被接口实现类进行覆盖重写

2.4 静态方法

创建格式:

1
2
3
4
5
格式:
public static 返回值类型 方法名称(参数列表){
方法体
}
tips:就是将abstract 或者default换成static即可,带上方法体。

注意:
1.不能通过实现类的的对象来调用接口当中的静态方法
2.正确调用方式:接口名.静态方法名(参数)。与之前学习的static静态方法的调用方式(类名称.静态方法)一样


2.5 私有方法

问题描述:我们需要一个共有方法,用来解决两个默认方法之间重复代码的问题。但是这个共有方法不应该让实现类使用,应该是私有化的

解决方案:从java9开始,接口当中允许定义私有方法
Ⅰ. 普通私有方法,解决多个默认方法之间重复代码问题

1
2
3
private 返回值类型 方法名(参数列表){
方法体
}

Ⅱ. 静态私有方法,解决多个静态方法之间的重复代码问题

1
2
3
private static 返回值类型 方法名(参数列表){
方法体
}

接口使用步骤

  • 接口不能直接使用,必须有一个“实现类”来“实现”该接口

格式:public class 实现类名 implements 接口名称{…}

  • 接口的实现类必须重写接口中所有的抽象方法(即去掉abstract关键字,加上方法体和大括号)

  • 创建实现类的对象,进行使用(即直接 new 实现类的对象,才能使用)。

  • 接口是没有静态代码块或者构造方法的

  • Java的类只能单继承,但接口可以implement多个接口
    接口的小结:

7.多态性的补充

代码当中体现多态性,其实就是一句话:父类引用指向子类对象(extends,implement是体现多态性的前提)

格式:父类引用指向子类对象(即以左父右子
父类名称. 对象名 = new 子类名称();
或者:
接口名称. 对象名 = new 实现类名称()

在多态的代码,父类引用指向子类对象是,即 Fu obj = new Zi() 中:

  1. 成员方法的访问规则: 看new的是谁(即看右边),就优先用谁,没有就向上一级找

  2. 成员变量的访问规则:
    Ⅰ. 直接通过对象名称访问成员变量,看等号左边是谁,优先用谁
    Ⅱ. 间接通过成员方法访问成员变量:看该方法属于谁(假如子类覆盖重写了父类,则通过访问子类调用成员方法来访问成员变量),就优先用谁

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
package com.Alibaba;

public class Mult {
public static void main(String[] args) {
Fu1 fu1 = new Zi1();
System.out.println(fu1.num); // 10:通过对象名称访问成员变量
fu1.showNum(); // 20
System.out.println("====");
// 开始覆盖重写父类
fu1.showNum(); // 20
}
}

// 创建父类
class Fu1{
int num = 10;
public void showNum(){
System.out.println(num);
}
}
// 创建子类
class Zi1 extends Fu1{
int num = 20;
@Override
public void showNum(){
System.out.println(num);
}
}

对象的向上转型

  1. 对象的向上转型,其实就是多态的写法;

格式:父类名称 对象名 = new 子类名称()

1
2
Animal animal = new Cat();
创建了一只猫,当作中午来看,是没问题的。

含义:右侧创建一个子类对象,把它当作父类来看待
注意事项;向上转型一定是安全的,从小范围转向了大范围。从小范围的猫,向上转换成为了范围更大的动物

对象的向下转型

  1. 对象的向下转型,其实是一个【还原】的动作

若想要实现向下转型的前提是有一个向上转型成功的对象名

格式:子类名称 对象名 = (子类名称) 父类对象名

1
2
Animal animal = new Cat(); //向上转型成功,将本来是猫,向上转型成为动物
Cat cat = (Cat) animal; // 本来是猫,已经被当做动物了,还原回来成为了原本的猫

注意事项;
Ⅰ. 必须保证对象本来创建的时候,就是猫,才能向下转型成为猫
Ⅱ. 如果对象创建的时候本来不是猫。现在非要向下转型成为猫,就会报错(ClassCastException)

  1. 用instanceof关键字判断一个父类的引用对象,本来是什么子类?

格式:对象名 instanceof 类名称

完整的源代码:

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
public class InstanceofDemo{
public static void main(String[] args){
// 多态写法;新建一只猫
Animal animal = new Cat();
animal.eat();

// 如果希望调用子类的特有方法,需要向下转型
if(animal instanceof Dog){ // 判断父类引用animal是不是Dog
Dog dog = (Dog) animal;
dog.watchHOuse();
}
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMOnth();
}

// 调用getMeAPet函数:传参为Dog()子类
getMeAPet(new Dog());
}

public static void getMeAPet(Animal animal){
if(animal instanceof Dog){ // 判断父类引用animal是不是Dog
Dog dog = (Dog) animal;
dog.watchHOuse();
}
if(animal instanceof Cat){
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
}

// 创建抽象父类
abstract class Animal{
public abstract void eat();
}
// 创建Cat子类
class Cat extends Animal{
@Override
public void eat(){
Systeam.out.println("猫吃鱼");
}

public void catchMouse(){
Systeam.out.println("猫抓老鼠吃");
}
}
//创建Dog子类
class Dog extends Animal{
@Override
public void Dog(){
Systeam.out.println("狗吃骨头");
}

public void watchHouse(){
Systeam.out.println("狗正在看家");
}
}

8.final关键字

final关键字代表最终,不可改变,常见的四种方法:

  • 可以用来修饰一个类

当final关键字用来修饰一个类时(表示当前这个类不能有任何子类,即无法继承),格式:
public final class 类名称{属性和方法}

  • 可以用来修饰一个方法

当final关键字修饰一个方法时,这个方法就是最终方法,也无法被覆盖重写,格式:
修饰符 final 返回值类型 方法名(参数列表){方法体}

  • 还可以修饰一个局部变量

一般局部变量:int age = 18; int age = 20(对于普通变量来说可以任意重新赋值)
final最终变量:一旦使用final用来修饰局部变量,那么这个变量就无法更改:final int age = 18

  • 还可以修饰一个成员变量

对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样不可变
由于成员变量具有默认值,所以采用final之后必须要手动赋值,不会再给默认值
对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值,二者选其一
必须保证类当中的所有重载的构造方法,都最终会对final的成员变量进行赋值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
当使用构造方法时,无参构造也要赋值,且因为final特性,构造函数就不存在Setter方法了
*/
class Person{
private final String name; /* = 鹿晗*/

public Person(){
name = "关晓彤"
}

public Person(String name){
this.name = name;
}

public String gerName(){
return name;
}
}

PS:
对于类和方法来说,abstract与final关键字不能同时使用,因为自相矛盾


 评论

联系我 | Contact with me

Copyright © 2019-2020 谁知你知我,我知你知深。此恨经年深,比情度日久

博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议