Java基础笔记9(类与对象)

面向对象

  • 面向对象编程(Object-Oriented Programing,OOP)
  • 面向对象编程的本质:以类的方式组织代码,以对象的形式组织(封装)数据
  • 三大特性
    • 封装
    • 继承
    • 多态
  • 对象是具体的事物,类是对对象的抽象
  • 从代码运行的角度,先有类再有对象,类是对象的模板

  • 属性:
    • 又叫字段(Field)或成员变量
    • 默认初始化
      • 数字:0、0.0
      • char:u0000
      • boolean:false
      • 引用:null
    • 修饰符 属性类型 属性名 = 属性值
  • 方法
    • 修饰符 返回类型 方法名(){ 方法体 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class 类名 {
//属性

//方法
}

//学生类(Student类)
public class Student {

private String name;//属性

public String getName() { return name; }//方法

public void setName() { //方法
this.name = name;
}
}

对象

  • 对象的创建和使用
    • 使用new关键字创建对象 Student stu = new Student();
    • 对象的属性stu.name; 前提属性name不是私有的
    • 对象的方法stu.getName();
1
2
3
4
5
6
7
8
9
10
//类实例化后会返回一个自己的对象
//zhangsan、lisi就是Student类的具体实例
Student zhangsan = new Student();
Student lisi = new Student();

zhangsan.setName("张三");
zhangsan.getName();//张三

lisi.setName("李四");
lisi.getName();//李四

构造器

  • 构造器名和类名相同,无返回值
  • 使用new关键字,本质是在调用构造器
  • 构造器用来初始化值
  • 一个类即使什么都不写,也会有一个默认构造方法(无参构造)
  • 一但定义了有参构造,无参构造就必须显式定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Student {

private String name;

public Student() {}//无参构造方法

public Student(String name) {//有参构造方法
this.name = name;
}

public String getName() { return name; }

public void setName() {
this.name = name;
}
}
1
2
Student zhangsan = new Student("张三");
zhangsan.getName();//张三

封装

  • 高内聚、低耦合

    • 高内聚:类内部的数据操作细节自己完成
    • 低耦合:暴露少量的方法供外部使用
  • 数据隐藏

    • 通常禁止直接访问一个对象中数据的实际表示,而应通过操作接口来进行访问

    • get\set

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public class Student {

      private String name;

      public String getName() { return name; }

      public void setName() {
      this.name = name;
      }
      }

继承

依赖、关联、组合、聚合

  • 继承是类与类之间的一种关系,除此之外类与类之间的关系还有依赖、关联、组合、聚合等

    • 依赖:

      • 类A中的方法使用到了另一个类B,B类的变化会影响到A,具有偶然性、临时性。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      //一般依赖关系在Java中体现为局域变量、方法的形参,或者对静态方法的调用。
      public class Money {
      public void count(){
      System.out.println("count money");
      }
      }

      public class Person {
      public void countMoney (Money money){//money作为Person类方法的参数。Person类依赖Money类
      money.count();
      }
      }
    • 关联:

      • 体现的是两个类、或者类与接口之间语义级别的一种强依赖关系。
      • 这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的
      • 一般是长期性的,而且双方的关系一般是平等的,关联可以是单向、双向的。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      //关联关系一般使用成员变量来实现。
      public class Money {
      public void count(){
      System.out.println("count money");
      }
      }

      public class Person {
      private Money money;

      public Person(Money money){
      this.money = money;
      }

      public void countMoney (){
      money.count();
      }
      }
    • 聚合:

      • 聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系
      • 在代码层面,聚合和关联关系是一致的,只能从语义级别来区分。普通的关联关系中,a类和b类没有必然的联系,而聚合中,需要b类是a类的一部分,是一种has a的关系,即 a has a b; 比如班级里有学生。
      • has a不是 must has a,a可以有b,也可以没有。a是整体,b是部分,整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。
      1
      2
      3
      4
      5
      6
      //不同于关联关系的平等地位,聚合关系中两个类的地位是不平等。
      public class MyClass {
      private List<Student> students; //一个家庭里有许多孩子

      //...
      }
  • 组合:

    • 组合也是关联关系的一种特例,他体现的是一种contains a的关系,这种关系比聚合更强,也称为强聚合。
    • 组合同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束。
    1
    2
    3
    4
    5
    6
    7
    //组合关系中,两个类关系也是不平等的。
    public class Person {
    private Arm arm;
    private Leg leg;//一个人有手有脚,但人没了,手和脚也就没意义了

    // ....
    }

继承

  • 继承的本质是对某一批类的抽象

  • 继承关系的两个类

    • 一个为子类(派生类)

    • 一个为父类(基类)

    • 子类继承父类,使用关键字extends来表示

    • 子类 extends 父类

      1
      2
      3
      public class Student extends Persion {
      //...
      }
    • 子类可以继承父类所有非私有属性和方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      //父类
      public class Father {
      public String lastName = "李";
      private String firstName = "四";
      protected int money = 2333;

      public void publicFun(){}
      private void privateFun(){}
      protected void protectedFun(){}
      }

      //子类
      public class Son extends Father {}

      //测试子类可以调用的父类中哪些属性和方法
      public class Test {
      public static void main(String[] args) {
      Son son = new Son();
      String lastName = son.lastName;
      int money = son.money;
      son.publicFun();
      son.protectedFun();
      }
      }
  • Java中类只有单继承,没有多继承

    • 一个子类只能直接继承一个父类,但可以间接继承多个父类
  • 子类和父类之间,从意义上讲应该具有“is a”的关系

    • Student is a Person
  • Object类

    • 在Java中,所有的类都默认直接或间接继承Object
  • super

    • 子类通过super来访问父类的方法和属性

      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
      public class Father {
      public String lastName = "李";
      public String firstName = "老四";

      public void test(){
      System.out.println("父类方法");
      System.out.println(firstName);
      System.out.println(this.firstName);
      }
      }

      public class Son extends Father {
      public String firstName = "小四";

      public void test(){
      System.out.println("子类方法");
      super.test();
      System.out.println("父类方法结束");
      System.out.println(firstName);
      System.out.println(this.firstName);
      System.out.println(super.firstName);
      }
      }

      public class Test {
      public static void main(String[] args) {
      Son son = new Son();
      son.test();
      }
      }
      /*
      输出:
      子类方法
      父类方法
      老四
      老四
      父类方法结束
      小四
      小四
      老四
      */
    • 子类的构造器必须调用父类的构造器;

    • 调用父类构造器必须是子类构造器第一行代码

    • 子类构造器默认调用父类无参构造super();

  • 方法重写

    • 需要继承关系,子类重写父类方法
    • 方法名必须相同
    • 参数列表必须相同
    • 修饰符范围可以扩大不能缩小
    • 抛出异常范围可以缩小但不可以扩大
    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
    public class B {
    public static void test(){
    System.out.println("B=>test()");
    }
    }

    public class A extends B {
    public static void test(){
    System.out.println("A=>test()");
    }
    }

    public class Test {
    public static void main(String[] args) {
    A a = new A();
    a.test();

    //父类的引用指向子类
    B b = new A();
    b.test();
    }
    }
    /*
    输出:
    A=>test()
    B=>test()
    */
    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
    //方法去掉static后
    public class B {
    public void test(){
    System.out.println("B=>test()");
    }
    }

    public class A extends B {
    public void test(){
    System.out.println("A=>test()");
    }
    }

    public class Test {
    public static void main(String[] args) {
    A a = new A();
    a.test();

    //父类的引用指向子类
    B b = new A();
    b.test();
    }
    }
    /*
    输出:
    A=>test()
    A=>test()

    静态方法是类的方法,非静态方法是对象的方法
    b是A new出来的对象,因此调用了A的方法
    */

多态

  • 即同一方法可以根据发送对象的不同而采用多种不同行为方式
  • 一个对象的实际类型是确定的,但可以指向的引用的类型有很多
  • 多态存在的条件
    • 有继承关系
    • 子类重写父类方法
    • 父类引用指向子类对象
  • 多态是方法的多态,属性没有多态性
  • 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
public class Father {
public void test1(){
System.out.println("Father=>test1()");
}
}

public class Son extends Father {
public void test1(){
System.out.println("Son=>test1()");
}

public void test2(){
System.out.println("Son=>test2()");
}
}

public class Test {
public static void main(String[] args) {
//一个对象的实际类型是确定的(new Son())
//引用类型不确定,父类引用指向子类
//son能调用自己的或继承父类的方法
Son son = new Son();
//father可以指向子类,但不能调用子类独有方法
Father father = new Son();
Object object = new Son();

//子类重写父类方法,执行子类方法
son.test1();
father.test1();

//对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
son.test2();
//father.test2(); father无test2()方法
}
}

instanceof

instanceof 用来测试一个对象是否为一个类的实例

1
boolean result = obj instanceof Class
  • obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。

  • 编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。

几种情况

  1. obj 必须为引用类型,不能是基本类型,否则编译不通过

    1
    2
    3
    int i = 0;
    System.out.println(i instanceof Integer);//编译不通过
    System.out.println(i instanceof Object);//编译不通过
  2. 如果 obj 为 null,那么将返回 false

    1
    System.out.println(null instanceof Object);//false
  3. obj 可以为 class 类的实例对象

    1
    2
    Integer integer = new Integer(1);
    System.out.println(integer instanceof Integer);//true
  4. obj 可以为 class 类的直接或间接子类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Father {}
    public class Son extends Father {}

    Father test1 = new Father();
    Father test2 = new Son();
    Son test3 = new Son();
    System.out.println(test1 instanceof Son);//false
    System.out.println(test2 instanceof Son);//true
    System.out.println(test3 instanceof Son);//true
  5. obj 可以为接口的实现类

    1
    2
    3
    4
    5
    6
    7
    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

    ArrayList arrayList = new ArrayList();
    System.out.println(arrayList instanceof List);//true

    List list = new ArrayList();
    System.out.println(list instanceof ArrayList);//true

static

  • 静态变量、静态方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public class Test {
    //static表示从属该类,跟类一起加载
    private int id;
    private static int sid;

    public void fn(){}

    public static void sfn(){}

    public static void main(String[] args) {
    int sid1 = sid;
    int sid2 = Test.sid;
    int id = new Test().id;

    sfn();
    Test.sfn();
    new Test().fn();
    }
    }
  • 静态代码块、匿名代码块

    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
    public class Test {
    //匿名代码块
    {
    System.out.println("匿名代码块");
    }

    //静态代码块 静态代码块只执行一次
    static {
    System.out.println("静态代码块");
    }

    Test() {
    System.out.println("构造方法");
    }

    public static void main(String[] args) {
    Test test1 = new Test();
    System.out.println("=============");
    Test test2 = new Test();
    }
    }
    /*
    输出:
    静态代码块
    匿名代码块
    构造方法
    =============
    匿名代码块
    构造方法
    */
  • 静态导入包

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //未导包前,调用Math.random()方法
    public class Test {
    public static void main(String[] args) {
    System.out.println(Math.random());
    }
    }
    //使用静态导入包后,调用Math.random()方法
    import static java.lang.Math.random;

    public class Test {
    public static void main(String[] args) {
    System.out.println(random());
    }
    }

抽象类

  • 抽象类不能new,只能靠子类去实现
  • 抽象类中可以写普通方法
  • 抽象方法必须写在抽象类中
  • 抽象类也有构造器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//abstract修饰抽象类
public abstract class Abstract {
//abstract修饰抽象方法,只有方法名,没有方法实现
public abstract void doIt();
}

//子类要继承实现抽象类的所有方法
public class Implement extends Abstract {
@Override
public void doIt() {

}
}

//或者子类也是抽象类,继续将方法传给自己的子类实现
public abstract class DontImplement extends Abstract {}

public class DoImplement extends DontImplement {
@Override
public void doIt() {

}
}

接口

  • 普通类:具体实现
  • 抽象类:具体实现和规范(抽象方法)
  • 接口:规范
  • 接口不能有构造方法,抽象类有构造方法
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
//interface 定义的关键字,接口需要有实现类
public interface UserService {
//接口里的所有属性都是常量public static final
int ID = 99;
//接口中的所有定义的方法都是public abstract
void add();
void delete();
void update();
void query();
}

public interface VIPService {
void vip();
}

//实现了接口中的类,就需要重写接口中的方法
//利用接口实现多继承
public class ServiceImpl implements UserService, VIPService {
@Override
public void add() {}
@Override
public void delete() {}
@Override
public void update() {}
@Override
public void query() {}
@Override
public void vip() {}
}

内部类

  • 成员内部类

  • 静态内部类

  • 局部内部类

  • 匿名内部类

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
public class Outer {
private int id;

public void out() {
System.out.println("外部类的方法");
//局部内部类
class Inner3 {
//局部内部类可以获得外部类的属性、方法
public void in() {
int inId = id;
out();
System.out.println("成员内部类方法");
}
}
}

//成员内部类
class Inner1 {
//成员内部类可以获得外部类的属性、方法
public void in() {
int inId = id;
out();
System.out.println("成员内部类方法");
}
}

//静态内部类
static class Inner2 {
//内部类可以获得外部类的静态属性、静态方法
public void in() {
System.out.println("成员内部类方法");
}
}

public static void main(String[] args) {
//匿名内部类,实现UserService接口
new Inner4() {
@Override
public void hide() {

}
};
}
}

interface Inner4 {
void hide();
}