Java_面向对象上(3)

构造方法

若没有定义构造方法,Java 将提供一个默认的无参构造方法,若重载了构造方法,Java 将不再提供默认的无参构造方法,建议永远自己给出无参构造方法

可通过 this 引用访问自身的成员变量和方法,通常可省略,除非是和局部变量同名才必须显示地调用来进行区分, this() 则表示调用自身的构造方法

 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 class Test {
  public static void main(String[] args) {
    Person p1 = new Person();
    p1.print(); //张三 20
    Person p2 = new Person("李四", 21);
    p2.print(); //李四 21
  }
}

class Person{
  private String name;
  private int age;
  public Person() {
    //this()的调用必须为构造方法的第一条语句
    this("张三", 20);
    System.out.println("无参构造方法");
  }
  public Person(String name, int age) {
    System.out.println("重载的有参构造方法");
    this.name = name; //用局部变量给成员变量赋值
    this.age = age;
  }
  public void print() {
    System.out.println(name + " " + age);
  }
}

成员变量和属性

成员变量,定义在方法外的变量,仅供本类的方法访问,即权限为 private

属性,外部可通过 get、set 方法访问的成员变量

 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 Test {
  public static void main(String[] args) {
    Person p = new Person();
    System.out.println(p.getName()); //null
    p.setName("张三");
    p.print(); //张三 20
  }
}

class Person {
  //属性
  private String name;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  //成员变量
  int age = 20;
  public void print() {
    System.out.println(name + " " + age);
  }
}

方法重载

重载 Overload 是在当前的类中定义多个入参不同的同名方法,入参不同是指类型,个数,顺序至少有一个不同,其中类型不同会涉及到类型匹配的优先级

基本类型的匹配优先级是 char -> int -> long -> float -> double,若找不到匹配的基本类型,则去找对应的包装类,若包装类也没有,则继续寻找类实现的接口 -> 父类,可变参数的优先级最低

 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
public class Test {
  public static void print(char arg) {
    System.out.println("char");
  }
  public static void print(int arg) {
    System.out.println("int");
  }
  public static void print(long arg) {
    System.out.println("long");
  }
  public static void print(float arg) {
    System.out.println("float");
  }
  public static void print(double arg) {
    System.out.println("double");
  }
  public static void print(Character arg) {
    System.out.println("Character");
  }
  public static void print(Serializable arg) {
    System.out.println("Serializable");
  }
  public static void print(Object arg) {
    System.out.println("object");
  }
  public static void print(char... arg) {
    System.out.println("char...");
  }
  public static void main(String[] args) {
    print('a');
  }
}

可逐一注释上面的重载方法来验证类型匹配的优先级,byte,short 也同理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Test {
  public static void print(byte arg) {
    System.out.println("byte");
  }
  public static void print(short arg) {
    System.out.println("short");
  }
  public static void print(int arg) {
    System.out.println("int");
  }
  //long
  //float
  //double
  //Byte
  //...
  public static void main(String[] args) {
    byte b = 1;
    print(b);
  }
}

注意,重载与返回值,修饰符没有关系,且仅在当前的类中,与其他类也没有关系

继承

Java 只支持单继承不支持多继承,即不能有多个父类,子类可以通过 super 引用访问父类的非私有成员变量,通过 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
public class Test {
  public static void main(String[] args) {
    Son s1 = new Son();
    Son s2 = new Son(10);
    s2.print(); //10
  }
}

class Father {
  private int num;
  public Father() {
    System.out.println("父类无参构造方法");
  }
  public Father(int num) {
    this.num = num;
  }
  public void print() {
    System.out.println(num);
  }
}

class Son extends Father {
  public Son() {
    System.out.println("子类无参构造方法");
  }
  public Son(int num) {
    //super.num = 10; //'num' has private access in 'Father'
    //同this(),super()的调用也必须为构造方法的第一条语句
    super(num); //调用父类有参构造方法
  }
}

上下转型

转型指的是左侧引用的改变,向上是父类引用指向子类对象,向下则是向上之后又变回来

 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 {
  public static void main(String[] args) {
    Son s = new Son();
    Father f = s; //向上转型,父类引用f指向子类对象s
    f.fprint(); //只能调用父类的方法
    //s.fprint(); //子类也能直接调用父类的方法
    //f.sprint(); //根据引用f类型检查Father没有sprint方法,编译报错
    s.sprint(); //根据引用s类型检查Son有sprint方法,编译通过

    //向下转型之前要先向上转型
    Father f1 = new Son(); //父类引用指向子类对象,向上转型
    Son s1 = (Son)f1;    //向下转型
    s1.fprint();
    s1.sprint();
    //不能直接向下转型
    Father f2 = new Father(); 
    Son s2 = (Son)f2;    //异常java.lang.ClassCastException
  }
}
class Father {
  public void fprint() {
    System.out.println("Father");
  }
}

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

方法重写和多态

重写 Override 是在子类中定义与父类方法同名,同参数的方法,且要求返回值类型和抛出的异常类型,重写方法要小于等于父类原方法,访问权限则要大于等于

重载在编译时就能确定调用哪个方法,重写只有在运行时才知道,若是子类对象则调用子类中重写的方法,若是父类对象则调用父类中的方法,与左边引用类型无关,只与调用这个方法的对象有关

多态要有继承,方法重写,父类引用指向子类对象,针对的是方法与成员变量无关,即左边引用若是父类则访问父类对应的成员变量,若是子类则访问子类的

对于方法则可分为静态绑定和动态绑定

静态绑定,例如 private、final、static 修饰的方法在编译时就能确定对应的类

动态绑定,例如 Father 类的引用 f 指向 Son 类的对象,而 Son 类重写了 print() 方法,f.print() 实际调用的是 Son 类重写的方法,而不是其父类 Father 中定义的方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Test {
  public static void main(String[] args) {
    Father f = new Son(); //向上转型
    Son s = new Son();
    f.print();  //Son,调用的还是子类重写的方法
    s.print();  //Son
    System.out.println(f.name); //Father,与成员变量无关,根据左边应用类型访问
  }
}
class Father {
  public String name = "Father";
  public void print() {
    System.out.println("Father");
  }
}
class Son extends Father {
  public String Son = "Son";
  @Override
  public void print() {
    System.out.println("Son");
  }
}

C++ 向上转型之后调用的就是父类的方法,与 Java 不同

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

using namespace std;
class Father {
  public: 
  void print() {
    cout << "Father" << endl;
  }
};
class Son: public Father {
  public: 
  void print() {
    cout << "Son" << endl;
  }
};

int main() {
  Son s;
  s.print(); //Son
  Father f = (Father) s; //向上转型
  f.print(); //Father
  return 0;
}

final

修饰类,表示该类不能被继承

修饰变量,表示该变量是常量,必须要初始化,且初始化之后不能再修改,基本类型不能修改值,引用类型不能修改引用的地址值,即不能指向别的对象,但能修改当前指向的对象本身

修饰方法,表示该方法不能被重写

 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 Test {
  public static void main(String[] args) {
    final Son s = new Son();
    //s.NUM = 2;   //报错,常量不能再修改
    //s = new S(); //报错,不能再指向其他对象
  }
}

/*final*/ class Father {
  public final void print() {
    System.out.println("father");
  }
}

class Son extends Father {
  //不能重写父类被final修饰的方法
  //public void print() {
  //System.out.println("son");
  //}

  //final变量默认初始化,之后不能再修改了,所以要显式初始化,或者在构造方法里初始化
  //常量命名规范,一个单词全部字母大写,多个单词全部字母大写,中间用下划线隔开
  public final int NUM = 1;
}

static

静态没有 this,可通过类名来访问

静态方法不能调用非静态成员变量,且不能被重写

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test {
  public static void main(String[] args) {
    Person.age = 20;
    Person p = new Person();
    p.print(); //张三 20
    Person.staticPrint(); //20
  }
}

class Person {
  public String name = "张三";
  public static int age; //静态成员
  public void print() {
    //非静态方法成员变量和静态成员变量都能调用
    System.out.println(name + " " + age);
  }
  public static void staticPrint() {
    //System.out.println(name); //报错,静态方法不能调用非静态成员变量
    System.out.println(age);
  }
}

以上内容是玉山整理的笔记,如有错误还请指出